From 0504d55d609d9dad2efee17309e4f135b449b965 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Wed, 30 Jan 2019 19:07:28 +0100
Subject: [PATCH] Implement typesafe SignedIds

---
 .../quasseldroid/dagger/ActivityModule.kt     |   2 +-
 .../kuschku/quasseldroid/defaults/Defaults.kt |   3 +-
 .../quasseldroid/service/BacklogRequester.kt  |  10 +-
 .../service/QuasselNotificationBackend.kt     |  11 +-
 .../quasseldroid/service/QuasselService.kt    |  24 +-
 .../QuasseldroidNotificationManager.kt        |   2 +-
 .../quasseldroid/ui/chat/ChatActivity.kt      |  37 ++-
 .../ui/chat/buffers/BufferListAdapter.kt      |   6 +-
 .../chat/buffers/BufferViewConfigFragment.kt  |  24 +-
 .../ui/chat/input/AutoCompleteHelper.kt       |   3 +-
 .../ui/chat/messages/MessageAdapter.kt        |   2 +-
 .../ui/chat/messages/MessageListFragment.kt   |  27 +-
 .../ui/chat/topic/TopicFragment.kt            |   3 +-
 .../ui/coresettings/CoreSettingsFragment.kt   |  14 +-
 .../ui/coresettings/SettingsItem.kt           |   4 +-
 .../ui/coresettings/SettingsItemAdapter.kt    |  19 +-
 .../chatlist/ChatListBaseFragment.kt          |   3 +-
 .../coresettings/chatlist/NetworkAdapter.kt   |   2 +-
 .../identity/IdentityBaseFragment.kt          |   3 +-
 .../identity/IdentityEditActivity.kt          |   2 +-
 .../coresettings/network/IdentityAdapter.kt   |   6 +-
 .../network/NetworkBaseFragment.kt            |   6 +-
 .../network/NetworkEditActivity.kt            |   2 +-
 .../ui/info/channel/ChannelInfoActivity.kt    |   2 +-
 .../ui/info/channel/ChannelInfoFragment.kt    |  10 +-
 .../info/channellist/ChannelListActivity.kt   |   2 +-
 .../ui/info/channellist/ChannelListAdapter.kt |   6 +-
 .../info/channellist/ChannelListFragment.kt   |   2 +-
 .../quasseldroid/ui/info/user/IrcUserInfo.kt  |   3 +-
 .../ui/info/user/UserInfoActivity.kt          |   4 +-
 .../ui/info/user/UserInfoFragment.kt          |  15 +-
 .../ui/setup/ServiceBoundSetupActivity.kt     |   2 +-
 .../ui/setup/ServiceBoundSlideFragment.kt     |   4 +-
 .../ui/setup/core/QuasselSetupEntry.kt        |   8 +-
 .../ui/setup/network/NetworkSetupActivity.kt  |  11 +-
 .../setup/network/NetworkSetupNetworkSlide.kt |  14 +-
 .../util/service/ServiceBoundActivity.kt      |   2 +-
 .../util/service/ServiceBoundFragment.kt      |   4 +-
 .../quasseldroid/util/AvatarHelperTest.kt     |  27 +-
 invokergenerator/build.gradle.kts             |  12 +-
 .../kuschku/libquassel/annotations/Context.kt |  29 ++
 .../kuschku/libquassel/annotations/Helpers.kt |  76 +++++
 .../annotations/InvokerProcessor.java         | 261 ------------------
 .../annotations/InvokerProcessor.kt           |  54 ++++
 .../annotations/data/ParsedClass.kt           |  28 ++
 .../annotations/data/ParsedMethod.kt          |  26 ++
 .../annotations/data/ParsedParameter.kt       |  27 ++
 .../annotations/generator/Generator.kt        | 112 ++++++++
 .../libquassel/annotations/parser/Parser.kt   | 255 +++++++++++++++++
 .../annotations/parser/ParserEnvironment.kt   |  52 ++++
 .../de/kuschku/libquassel/protocol/QType.kt   |   8 +-
 .../de/kuschku/libquassel/protocol/QTypes.kt  |   8 -
 .../kuschku/libquassel/protocol/SignedId.kt   |  87 ++++++
 .../serializer/BufferIdSerializer.kt          |  35 +++
 .../serializer/BufferInfoSerializer.kt        |   8 +-
 .../serializer/IdentityIdSerializer.kt        |  35 +++
 .../primitive/serializer/MessageSerializer.kt |   4 +-
 .../primitive/serializer/MsgIdSerializer.kt   |  35 +++
 .../serializer/NetworkIdSerializer.kt         |  35 +++
 .../serializer/SignedIdSerializer.kt          |  35 +++
 .../kuschku/libquassel/quassel/BufferInfo.kt  |   6 +-
 .../quassel/syncables/BacklogManager.kt       |  31 ++-
 .../quassel/syncables/BufferSyncer.kt         |  18 +-
 .../quassel/syncables/BufferViewConfig.kt     |   6 +-
 .../quassel/syncables/CertManager.kt          |   2 +-
 .../libquassel/quassel/syncables/Identity.kt  |   4 +-
 .../quassel/syncables/IrcChannel.kt           |   2 +-
 .../quassel/syncables/IrcListHelper.kt        |   4 +-
 .../libquassel/quassel/syncables/IrcUser.kt   |   2 +-
 .../libquassel/quassel/syncables/Network.kt   |   8 +-
 .../syncables/interfaces/IBacklogManager.kt   |  21 +-
 .../quassel/syncables/interfaces/INetwork.kt  |   4 +-
 .../syncables/interfaces/IRpcHandler.kt       |   2 +-
 .../libquassel/session/BacklogStorage.kt      |   2 +-
 .../de/kuschku/libquassel/session/Session.kt  |   2 +-
 .../serializer/BufferInfoSerializerTest.kt    |  10 +-
 .../serializer/MessageSerializerTest.kt       |  14 +-
 .../quassel/syncables/AliasManagerTest.kt     |   6 +-
 .../quassel/syncables/BufferViewConfigTest.kt |   5 +-
 .../quassel/syncables/IdentityTest.kt         |   5 +-
 .../quassel/syncables/NetworkTest.kt          |  10 +-
 .../syncables/interfaces/INetworkInfoTest.kt  |   5 +-
 .../kuschku/libquassel/util/RandomHelpers.kt  |   2 +-
 .../persistence/QuasselBacklogStorage.kt      |  11 +-
 .../persistence/QuasselDatabase.kt            | 188 ++++++++++---
 .../persistence/QuasselDatabaseHelpers.kt     |  91 ++++++
 .../quasseldroid/viewmodel/EditorViewModel.kt |   3 +-
 .../viewmodel/QuasselViewModel.kt             |  16 +-
 88 files changed, 1462 insertions(+), 571 deletions(-)
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
 create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt
 create mode 100644 persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt

diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
index eb5e6e12a..3feea1e69 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
@@ -237,7 +237,7 @@ abstract class ActivityModule {
   @ContributesAndroidInjector(modules = [CoreSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
   abstract fun bindCoreSetupActivity(): CoreSetupActivity
 
-   // Service
+  // Service
 
   @ActivityScope
   @ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class])
diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
index 4c575575b..7931c2561 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt
@@ -22,6 +22,7 @@ package de.kuschku.quasseldroid.defaults
 import android.content.Context
 import de.kuschku.libquassel.protocol.Buffer_Activity
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
@@ -72,7 +73,7 @@ object Defaults {
     }
 
   fun network(context: Context, proxy: SignalProxy = SignalProxy.NULL) =
-    Network(-1, proxy).apply {
+    Network(NetworkId(-1), proxy).apply {
       setNetworkName("")
     }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt
index 1b0308f40..b61d63ee2 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt
@@ -26,9 +26,7 @@ import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG
 import de.kuschku.libquassel.util.helpers.value
-import de.kuschku.quasseldroid.persistence.AccountDatabase
-import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage
-import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.persistence.*
 import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
 
 class BacklogRequester(
@@ -52,9 +50,9 @@ class BacklogRequester(
                                                ?: 0)
         it.requestBacklog(
           bufferId = buffer,
-          last = lastMessageId ?: database.message().findFirstByBufferId(
-            buffer
-          )?.messageId ?: -1,
+          last = lastMessageId
+                 ?: database.message().findFirstByBufferId(buffer)?.messageId
+                 ?: MsgId(-1),
           limit = amount
         ) {
           if (it.isNotEmpty()) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
index 24e67bb29..e4c308a31 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
@@ -38,6 +38,9 @@ import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.GlideRequest
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.persistence.all
+import de.kuschku.quasseldroid.persistence.buffers
+import de.kuschku.quasseldroid.persistence.markRead
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.settings.NotificationSettings
@@ -122,7 +125,7 @@ class QuasselNotificationBackend @Inject constructor(
                 activity.hasFlag(Message_Type.Action) ||
                 activity.hasFlag(Message_Type.Notice))
               session.backlogManager.requestBacklogFiltered(
-                buffer.bufferId, lastSeenId, -1, 20, 0,
+                buffer.bufferId, lastSeenId, MsgId(-1), 20, 0,
                 Message_Type.of(Message_Type.Plain,
                                 Message_Type.Action,
                                 Message_Type.Notice).toInt(),
@@ -136,7 +139,7 @@ class QuasselNotificationBackend @Inject constructor(
             val highlightCount = session.bufferSyncer.highlightCount(buffer.bufferId)
             if (highlightCount != 0) {
               session.backlogManager.requestBacklogFiltered(
-                buffer.bufferId, lastSeenId, -1, 20, 0,
+                buffer.bufferId, lastSeenId, MsgId(-1), 20, 0,
                 Message_Type.of(Message_Type.Plain,
                                 Message_Type.Action,
                                 Message_Type.Notice).toInt(),
@@ -224,7 +227,7 @@ class QuasselNotificationBackend @Inject constructor(
     }.map {
       val network = session.network(it.bufferInfo.networkId)
       val me = network?.me()
-      QuasselDatabase.NotificationData(
+      QuasselDatabase.NotificationData.of(
         messageId = it.messageId,
         creationTime = now,
         time = it.time,
@@ -369,7 +372,7 @@ class QuasselNotificationBackend @Inject constructor(
         notificationSettings, buffer, selfInfo, notificationData, isLoud, isConnected
       )
       notificationHandler.notify(notification)
-    } ?: notificationHandler.remove(buffer)
+    } ?: notificationHandler.remove(buffer.id)
   }
 
   @Synchronized
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
index 8a990c698..a2726b36f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
@@ -45,9 +45,7 @@ import de.kuschku.quasseldroid.BuildConfig
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.Defaults
-import de.kuschku.quasseldroid.persistence.AccountDatabase
-import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage
-import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.persistence.*
 import de.kuschku.quasseldroid.settings.ConnectionSettings
 import de.kuschku.quasseldroid.settings.NotificationSettings
 import de.kuschku.quasseldroid.settings.Settings
@@ -172,10 +170,10 @@ class QuasselService : DaggerLifecycleService(),
       }
     }
 
-    val bufferId = intent.getIntExtra("buffer", -1)
+    val bufferId = BufferId(intent.getIntExtra("buffer", -1))
 
     val inputResults = RemoteInput.getResultsFromIntent(intent)?.getCharSequence("reply_content")
-    if (inputResults != null && bufferId != -1) {
+    if (inputResults != null && bufferId.isValidId()) {
       if (inputResults.isNotBlank()) {
         val lines = inputResults.lineSequence().map {
           it.toString() to ircFormatSerializer.toEscapeCodes(SpannableString(it))
@@ -197,14 +195,14 @@ class QuasselService : DaggerLifecycleService(),
         }
       }
     } else {
-      val clearMessageId = intent.getLongExtra("mark_read_message", -1)
-      if (bufferId != -1 && clearMessageId != -1L) {
+      val clearMessageId = MsgId(intent.getLongExtra("mark_read_message", -1))
+      if (bufferId.isValidId() && clearMessageId.isValidId()) {
         sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, clearMessageId)
         sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
       }
 
-      val hideMessageId = intent.getLongExtra("hide_message", -1)
-      if (bufferId != -1 && hideMessageId != -1L) {
+      val hideMessageId = MsgId(intent.getLongExtra("hide_message", -1))
+      if (bufferId.isValidId() && hideMessageId.isValidId()) {
         if (notificationSettings.markReadOnSwipe) {
           sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, hideMessageId)
           sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
@@ -501,7 +499,7 @@ class QuasselService : DaggerLifecycleService(),
     val deletedBuffersMessage = database.message().buffers().toSet() - buffers
     log(INFO, "QuasselService", "Buffers deleted from message storage: $deletedBuffersMessage")
     for (deletedBuffer in deletedBuffersMessage) {
-      database.message().clearMessages(deletedBuffer)
+      database.message().clearMessages(deletedBuffer.id)
     }
 
     val deletedBuffersFiltered = database.filtered().buffers(accountId).toSet() - buffers
@@ -546,13 +544,13 @@ class QuasselService : DaggerLifecycleService(),
         putExtra("disconnect", disconnect)
       }
       if (bufferId != null) {
-        putExtra("buffer", bufferId)
+        putExtra("buffer", bufferId.id)
       }
       if (markReadMessage != null) {
-        putExtra("mark_read_message", markReadMessage)
+        putExtra("mark_read_message", markReadMessage.id)
       }
       if (hideMessage != null) {
-        putExtra("hide_message", hideMessage)
+        putExtra("hide_message", hideMessage.id)
       }
     }
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
index 99817f5a5..c7261280d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
@@ -237,7 +237,7 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
           }
         }
       }
-    return Handle(buffer.id, notification)
+    return Handle(buffer.id.id, notification)
   }
 
   fun notificationBackground(): Handle {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 3f55b80e6..5d48d4176 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -48,10 +48,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
 import de.kuschku.libquassel.connection.ConnectionState
 import de.kuschku.libquassel.connection.ProtocolVersionException
 import de.kuschku.libquassel.connection.QuasselSecurityException
-import de.kuschku.libquassel.protocol.Buffer_Type
-import de.kuschku.libquassel.protocol.Message
-import de.kuschku.libquassel.protocol.Message_Type
-import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.*
 import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
 import de.kuschku.libquassel.protocol.message.HandshakeMessage
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT
@@ -67,8 +64,7 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.DefaultNetworkServer
-import de.kuschku.quasseldroid.persistence.AccountDatabase
-import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.persistence.*
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.settings.Settings
@@ -161,7 +157,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
           }
         }
         intent.hasExtra(KEY_BUFFER_ID)                                  -> {
-          viewModel.buffer.onNext(intent.getIntExtra(KEY_BUFFER_ID, -1))
+          viewModel.buffer.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1)))
           viewModel.bufferOpened.onNext(Unit)
           if (intent.hasExtra(KEY_ACCOUNT_ID)) {
             val accountId = intent.getLongExtra(ChatActivity.KEY_ACCOUNT_ID, -1)
@@ -183,7 +179,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
           drawerLayout.closeDrawers()
         }
         intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL) -> {
-          val networkId = intent.getIntExtra(KEY_NETWORK_ID, -1)
+          val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
           val channel = intent.getStringExtra(KEY_CHANNEL)
 
           viewModel.session.filter(Optional<ISession>::isPresent).firstElement().subscribe {
@@ -310,9 +306,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     // If we connect to a new network without statusbuffer, the bufferid may be -networkId.
     // In that case, once we’re connected (and a status buffer exists), we want to switch to it.
     combineLatest(viewModel.allBuffers, viewModel.buffer).map { (buffers, current) ->
-      if (current > 0) Optional.empty()
+      if (current.isValidId()) Optional.empty()
       else Optional.ofNullable(buffers.firstOrNull {
-        it.networkId == -current && it.type.hasFlag(Buffer_Type.StatusBuffer)
+        it.networkId == NetworkId(-current.id) && it.type.hasFlag(Buffer_Type.StatusBuffer)
       })
     }.toLiveData().observe(this, Observer { info ->
       info?.orNull()?.let {
@@ -632,7 +628,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       .observe(this, Observer {
         if (connectedAccount != accountId) {
           if (resources.getBoolean(R.bool.buffer_drawer_exists) &&
-              viewModel.buffer.value == Int.MAX_VALUE &&
+              viewModel.buffer.value == BufferId.MAX_VALUE &&
               !restoredDrawerState) {
             drawerLayout.openDrawer(GravityCompat.START)
           }
@@ -782,7 +778,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
   override fun onSaveInstanceState(outState: Bundle?) {
     super.onSaveInstanceState(outState)
-    outState?.putInt(KEY_OPEN_BUFFER, viewModel.buffer.value ?: -1)
+    outState?.putInt(KEY_OPEN_BUFFER, viewModel.buffer.value.id ?: -1)
     outState?.putInt(KEY_OPEN_BUFFERVIEWCONFIG, viewModel.bufferViewConfigId.value ?: -1)
     outState?.putLong(KEY_CONNECTED_ACCOUNT, connectedAccount)
     outState?.putBoolean(KEY_OPEN_DRAWER_START, drawerLayout.isDrawerOpen(GravityCompat.START))
@@ -791,7 +787,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
   override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
     super.onRestoreInstanceState(savedInstanceState)
-    viewModel.buffer.onNext(savedInstanceState?.getInt(KEY_OPEN_BUFFER, -1) ?: -1)
+    viewModel.buffer.onNext(BufferId(savedInstanceState?.getInt(KEY_OPEN_BUFFER, -1) ?: -1))
     viewModel.bufferViewConfigId.onNext(savedInstanceState?.getInt(KEY_OPEN_BUFFERVIEWCONFIG, -1)
                                         ?: -1)
     connectedAccount = savedInstanceState?.getLong(KEY_CONNECTED_ACCOUNT, -1L) ?: -1L
@@ -816,7 +812,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     }
 
     menuInflater.inflate(R.menu.activity_main, menu)
-    menu?.findItem(R.id.action_nicklist)?.isVisible = bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false
+    menu?.findItem(R.id.action_nicklist)?.isVisible = bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer)
+                                                      ?: false
     menu?.findItem(R.id.action_filter_messages)?.isVisible =
       (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false ||
        bufferData?.info?.type?.hasFlag(Buffer_Type.QueryBuffer) ?: false)
@@ -895,7 +892,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   .fold(Message_Type.of()) { acc, i -> acc or i }
 
                 database.filtered().replace(
-                  QuasselDatabase.Filtered(accountId, buffer, newlyFiltered.value.toInt())
+                  QuasselDatabase.Filtered.of(accountId, buffer, newlyFiltered.value.toInt())
                 )
               }
             }
@@ -964,7 +961,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     startedSelection = true
     connectedAccount = -1L
     restoredDrawerState = false
-    ChatActivity.launch(this, bufferId = Int.MAX_VALUE)
+    ChatActivity.launch(this, bufferId = BufferId.MAX_VALUE)
     viewModel.resetAccount()
   }
 
@@ -998,7 +995,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       autoCompleteSuffix: String? = null,
       channel: String? = null,
       networkId: NetworkId? = null,
-      bufferId: Int? = null,
+      bufferId: BufferId? = null,
       accountId: Long? = null
     ) = context.startActivity(
       intent(context,
@@ -1018,7 +1015,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       autoCompleteSuffix: String? = null,
       channel: String? = null,
       networkId: NetworkId? = null,
-      bufferId: Int? = null,
+      bufferId: BufferId? = null,
       accountId: Long? = null
     ) = Intent(context, ChatActivity::class.java).apply {
       if (sharedText != null) {
@@ -1032,13 +1029,13 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
         }
       }
       if (bufferId != null) {
-        putExtra(KEY_BUFFER_ID, bufferId)
+        putExtra(KEY_BUFFER_ID, bufferId.id)
         if (accountId != null) {
           putExtra(KEY_ACCOUNT_ID, accountId)
         }
       }
       if (networkId != null && channel != null) {
-        putExtra(KEY_NETWORK_ID, networkId)
+        putExtra(KEY_NETWORK_ID, networkId.id)
         putExtra(KEY_CHANNEL, channel)
       }
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
index d3fda7c5f..aa3b82bd7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
@@ -84,13 +84,13 @@ class BufferListAdapter(
   }
 
   fun toggleSelection(buffer: BufferId): Boolean {
-    val next = if (selectedBuffer.value == buffer) Int.MAX_VALUE else buffer
+    val next = if (selectedBuffer.value == buffer) BufferId.MAX_VALUE else buffer
     selectedBuffer.onNext(next)
-    return next != Int.MAX_VALUE
+    return next != BufferId.MAX_VALUE
   }
 
   fun unselectAll() {
-    selectedBuffer.onNext(Int.MAX_VALUE)
+    selectedBuffer.onNext(BufferId.MAX_VALUE)
   }
 
   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BufferViewHolder {
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 60cfaeacd..ada92099e 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
@@ -120,41 +120,41 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
 
       return if (info != null && session != null) {
         when (item?.itemId) {
-          R.id.action_channellist  -> {
+          R.id.action_channellist -> {
             network?.let {
               ChannelListActivity.launch(requireContext(), network = it.networkId())
             }
             actionMode?.finish()
             true
           }
-          R.id.action_configure  -> {
+          R.id.action_configure   -> {
             network?.let {
               NetworkEditActivity.launch(requireContext(), network = it.networkId())
             }
             actionMode?.finish()
             true
           }
-          R.id.action_connect    -> {
+          R.id.action_connect     -> {
             network?.requestConnect()
             actionMode?.finish()
             true
           }
-          R.id.action_disconnect -> {
+          R.id.action_disconnect  -> {
             network?.requestDisconnect()
             actionMode?.finish()
             true
           }
-          R.id.action_join       -> {
+          R.id.action_join        -> {
             session.rpcHandler?.sendInput(info, "/join ${info.bufferName}")
             actionMode?.finish()
             true
           }
-          R.id.action_part       -> {
+          R.id.action_part        -> {
             session.rpcHandler?.sendInput(info, "/part ${info.bufferName}")
             actionMode?.finish()
             true
           }
-          R.id.action_delete     -> {
+          R.id.action_delete      -> {
             MaterialDialog.Builder(activity!!)
               .content(R.string.buffer_delete_confirmation)
               .positiveText(R.string.label_yes)
@@ -174,7 +174,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
               .show()
             true
           }
-          R.id.action_rename     -> {
+          R.id.action_rename      -> {
             MaterialDialog.Builder(activity!!)
               .input(
                 getString(R.string.label_buffer_name),
@@ -197,24 +197,24 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
               .show()
             true
           }
-          R.id.action_unhide     -> {
+          R.id.action_unhide      -> {
             bufferSyncer?.let {
               bufferViewConfig?.orNull()?.insertBufferSorted(info, bufferSyncer)
             }
             actionMode?.finish()
             true
           }
-          R.id.action_hide_temp  -> {
+          R.id.action_hide_temp   -> {
             bufferViewConfig?.orNull()?.requestRemoveBuffer(info.bufferId)
             actionMode?.finish()
             true
           }
-          R.id.action_hide_perm  -> {
+          R.id.action_hide_perm   -> {
             bufferViewConfig?.orNull()?.requestRemoveBufferPermanently(info.bufferId)
             actionMode?.finish()
             true
           }
-          else                   -> false
+          else                    -> false
         }
       } else {
         false
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 5f14a2352..1137b959d 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
@@ -25,6 +25,7 @@ import android.text.style.ForegroundColorSpan
 import android.text.style.StyleSpan
 import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.Observer
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.quassel.syncables.IrcUser
@@ -168,7 +169,7 @@ class AutoCompleteHelper(
     this.dataListeners += listener
   }
 
-  private fun fullAutoComplete(sessionOptional: Optional<ISession>, id: Int,
+  private fun fullAutoComplete(sessionOptional: Optional<ISession>, id: BufferId,
                                lastWord: Pair<String, IntRange>): List<AutoCompleteItem> {
     val session = sessionOptional.orNull()
     val bufferSyncer = session?.bufferSyncer
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
index 0fec43779..95e31b796 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
@@ -120,7 +120,7 @@ class MessageAdapter @Inject constructor(
   } ?: 0
 
   override fun getItemId(position: Int): Long {
-    return getItem(position)?.content?.messageId ?: 0L
+    return getItem(position)?.content?.messageId?.id ?: 0L
   }
 
   private fun messageType(viewType: Int): Message_Type? =
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 0dbfd3d77..2636c2c55 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
@@ -55,8 +55,7 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.R
-import de.kuschku.quasseldroid.persistence.AccountDatabase
-import de.kuschku.quasseldroid.persistence.QuasselDatabase
+import de.kuschku.quasseldroid.persistence.*
 import de.kuschku.quasseldroid.service.BacklogRequester
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
@@ -290,11 +289,11 @@ class MessageListFragment : ServiceBoundFragment() {
     }
     adapter.setOnUrlLongClickListener(LinkLongClickMenuHelper())
 
-    adapter.setOnExpansionListener { (messageId) ->
+    adapter.setOnExpansionListener {
       val value = viewModel.expandedMessages.value
       viewModel.expandedMessages.onNext(
-        if (value.contains(messageId)) value - messageId
-        else value + messageId
+        if (value.contains(it.messageId)) value - it.messageId
+        else value + it.messageId
       )
     }
 
@@ -392,7 +391,7 @@ class MessageListFragment : ServiceBoundFragment() {
     }
 
     viewModel.buffer.toLiveData().observe(this, Observer { bufferId ->
-      swipeRefreshLayout.isEnabled = (bufferId != null || bufferId != -1)
+      swipeRefreshLayout.isEnabled = (bufferId != null || bufferId?.isValidId() == true)
     })
 
     viewModel.sessionManager.mapSwitchMap(SessionManager::state).distinctUntilChanged().toLiveData().observe(
@@ -407,7 +406,7 @@ class MessageListFragment : ServiceBoundFragment() {
             // Try loading messages when switching to isEmpty buffer
             val hasVisibleMessages = database.message().hasVisibleMessages(bufferId, filtered)
             if (!hasVisibleMessages) {
-              if (bufferId > 0 && bufferId != Int.MAX_VALUE) {
+              if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) {
                 loadMore(initial = true)
               }
             }
@@ -484,7 +483,7 @@ class MessageListFragment : ServiceBoundFragment() {
       (messageList.layoutManager as RecyclerView.LayoutManager).onRestoreInstanceState(getParcelable(
         KEY_STATE_LIST))
       previousLoadKey = getInt(KEY_STATE_PAGING).nullIf { it == -1 }
-      lastBuffer = getInt(KEY_STATE_BUFFER).nullIf { it == -1 }
+      lastBuffer = BufferId(getInt(KEY_STATE_BUFFER)).nullIf { !it.isValidId() }
     }
 
     data.observe(this, Observer { list ->
@@ -496,7 +495,8 @@ class MessageListFragment : ServiceBoundFragment() {
           list?.let(adapter::submitList)
         }
 
-        val buffer = viewModel.buffer.value ?: -1
+        val buffer = viewModel.buffer.value
+                     ?: BufferId(-1)
         if (buffer != lastBuffer) {
           adapter.clearCache()
           viewModel.session.value?.orNull()?.bufferSyncer?.let { bufferSyncer ->
@@ -514,7 +514,7 @@ class MessageListFragment : ServiceBoundFragment() {
     super.onSaveInstanceState(outState)
     outState.putParcelable(KEY_STATE_LIST, messageList.layoutManager?.onSaveInstanceState())
     outState.putInt(KEY_STATE_PAGING, previousLoadKey ?: -1)
-    outState.putInt(KEY_STATE_BUFFER, lastBuffer ?: -1)
+    outState.putInt(KEY_STATE_BUFFER, lastBuffer?.id ?: -1)
   }
 
   private fun markAsRead(bufferSyncer: BufferSyncer, buffer: BufferId, lastMessageId: MsgId?) {
@@ -537,7 +537,7 @@ class MessageListFragment : ServiceBoundFragment() {
                                            ?: 0)
     val hasVisibleMessages = database.message().hasVisibleMessages(current, filtered)
     if (!hasVisibleMessages) {
-      if (current > 0 && current != Int.MAX_VALUE) {
+      if (current.isValidId() && current != BufferId.MAX_VALUE) {
         loadMore(initial = true)
       }
     }
@@ -559,7 +559,7 @@ class MessageListFragment : ServiceBoundFragment() {
     // This can be called *after* we’re already detached from the activity
     activity?.runOnUiThread {
       viewModel.buffer { bufferId ->
-        if (bufferId > 0 && bufferId != Int.MAX_VALUE) {
+        if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) {
           if (initial) swipeRefreshLayout.isRefreshing = true
           runInBackground {
             backlogRequester.loadMore(
@@ -568,7 +568,8 @@ class MessageListFragment : ServiceBoundFragment() {
               amount = if (initial) backlogSettings.initialAmount else backlogSettings.pageSize,
               pageSize = backlogSettings.pageSize,
               lastMessageId = lastMessageId
-                              ?: database.message().findFirstByBufferId(bufferId)?.messageId ?: -1,
+                              ?: database.message().findFirstByBufferId(bufferId)?.messageId
+                              ?: MsgId(-1),
               untilAllVisible = initial
             ) {
               activity?.runOnUiThread {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
index c3cda191e..b8ca3f596 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
@@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.google.android.material.bottomsheet.BottomSheetBehavior
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
@@ -117,7 +118,7 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable {
       }
     }
 
-    val bufferId = arguments?.getInt("buffer", -1) ?: -1
+    val bufferId = BufferId(arguments?.getInt("buffer", -1) ?: -1)
     viewModel.buffer.onNext(bufferId)
     viewModel.bufferData.filter {
       it.info != null
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 d9a42f420..33ce70726 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
@@ -31,6 +31,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
@@ -102,15 +104,15 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     val view = inflater.inflate(R.layout.settings_list, container, false)
     ButterKnife.bind(this, view)
 
-    val networkAdapter = SettingsItemAdapter {
+    val networkAdapter = SettingsItemAdapter<NetworkId> {
       NetworkEditActivity.launch(requireContext(), network = it)
     }
 
-    val identityAdapter = SettingsItemAdapter {
+    val identityAdapter = SettingsItemAdapter<IdentityId> {
       IdentityEditActivity.launch(requireContext(), identity = it)
     }
 
-    val chatListAdapter = SettingsItemAdapter {
+    val chatListAdapter = SettingsItemAdapter<Int> {
       ChatlistEditActivity.launch(requireContext(), chatlist = it)
     }
 
@@ -136,7 +138,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
         combineLatest(it.values.map(Network::liveNetworkInfo)).map {
           it.map {
             SettingsItem(it.networkId, it.networkName)
-          }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, SettingsItem::name))
+          }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, SettingsItem<NetworkId>::name))
         }
       }
     }.toLiveData().observe(this, Observer {
@@ -155,7 +157,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
         combineLatest(it.values.map(Identity::liveUpdates)).map {
           it.map {
             SettingsItem(it.id(), it.identityName() ?: "")
-          }.sortedBy(SettingsItem::name)
+          }.sortedBy(SettingsItem<IdentityId>::name)
         }
       }
     }.toLiveData().observe(this, Observer {
@@ -171,7 +173,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
       combineLatest(it.values.map(BufferViewConfig::liveUpdates)).map {
         it.map {
           SettingsItem(it.bufferViewId(), it.bufferViewName())
-        }.sortedBy(SettingsItem::name)
+        }.sortedBy(SettingsItem<Int>::name)
       }
     }.toLiveData().observe(this, Observer {
       chatListAdapter.submitList(it.orEmpty())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt
index 6020cdc3b..9dec46e60 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt
@@ -19,7 +19,7 @@
 
 package de.kuschku.quasseldroid.ui.coresettings
 
-data class SettingsItem(
-  val id: Int,
+data class SettingsItem<T>(
+  val id: T,
   val name: String
 )
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt
index 16d7046a1..42b2f8c3d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt
@@ -28,16 +28,15 @@ import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
-import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.quasseldroid.R
 
-class SettingsItemAdapter(private val clickListener: (Int) -> Unit) :
-  ListAdapter<SettingsItem, SettingsItemAdapter.SettingsItemViewHolder>(
-    object : DiffUtil.ItemCallback<SettingsItem>() {
-      override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem) =
+class SettingsItemAdapter<T>(private val clickListener: (T) -> Unit) :
+  ListAdapter<SettingsItem<T>, SettingsItemAdapter.SettingsItemViewHolder<T>>(
+    object : DiffUtil.ItemCallback<SettingsItem<T>>() {
+      override fun areItemsTheSame(oldItem: SettingsItem<T>, newItem: SettingsItem<T>) =
         oldItem.id == newItem.id
 
-      override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem) =
+      override fun areContentsTheSame(oldItem: SettingsItem<T>, newItem: SettingsItem<T>) =
         oldItem == newItem
     }
   ) {
@@ -46,16 +45,16 @@ class SettingsItemAdapter(private val clickListener: (Int) -> Unit) :
     clickListener
   )
 
-  override fun onBindViewHolder(holder: SettingsItemViewHolder, position: Int) {
+  override fun onBindViewHolder(holder: SettingsItemViewHolder<T>, position: Int) {
     holder.bind(getItem(position))
   }
 
-  class SettingsItemViewHolder(itemView: View, clickListener: (Int) -> Unit) :
+  class SettingsItemViewHolder<T>(itemView: View, clickListener: (T) -> Unit) :
     RecyclerView.ViewHolder(itemView) {
     @BindView(R.id.title)
     lateinit var title: TextView
 
-    var id: IdentityId? = null
+    var id: T? = null
 
     init {
       ButterKnife.bind(this, itemView)
@@ -64,7 +63,7 @@ class SettingsItemAdapter(private val clickListener: (Int) -> Unit) :
       }
     }
 
-    fun bind(item: SettingsItem) {
+    fun bind(item: SettingsItem<T>) {
       this.id = item.id
       this.title.text = item.name
     }
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 9070b0af0..d624d7078 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
@@ -32,6 +32,7 @@ import butterknife.BindView
 import butterknife.ButterKnife
 import de.kuschku.libquassel.protocol.Buffer_Activity
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
@@ -224,7 +225,7 @@ abstract class ChatListBaseFragment(private val initDefault: Boolean) :
     else allowedBufferTypes -= Buffer_Type.StatusBuffer
     data.setAllowedBufferTypes(allowedBufferTypes)
 
-    data.setNetworkId(networkId.selectedItemId.toInt())
+    data.setNetworkId(NetworkId(networkId.selectedItemId.toInt()))
     data.setMinimumActivity(minimumActivity.selectedItemId.toInt())
 
     if (old != null) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt
index aedf2d7c6..3d14d42d8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt
@@ -71,7 +71,7 @@ class NetworkAdapter(@StringRes private val fallbackName: Int) :
   }
 
   override fun getItem(position: Int): INetwork.NetworkInfo? = data[position]
-  override fun getItemId(position: Int) = getItem(position)?.networkId?.toLong() ?: -1
+  override fun getItemId(position: Int) = getItem(position)?.networkId?.id?.toLong() ?: -1
   override fun hasStableIds() = true
   override fun getCount() = data.size
   class NetworkViewHolder(@StringRes private val fallbackName: Int, itemView: View) :
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
index 9b2bfb294..1b2154fe2 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt
@@ -34,6 +34,7 @@ import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.afollestad.materialdialogs.MaterialDialog
+import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
@@ -94,7 +95,7 @@ abstract class IdentityBaseFragment(private val initDefault: Boolean) :
     val view = inflater.inflate(R.layout.settings_identity, container, false)
     ButterKnife.bind(this, view)
 
-    val identityId = arguments?.getInt("identity", -1) ?: -1
+    val identityId = IdentityId(arguments?.getInt("identity", -1) ?: -1)
 
     adapter = IdentityNicksAdapter(::nickClick, ::startDrag)
     nicks.layoutManager = LinearLayoutManager(requireContext())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt
index 5e4c2c0ce..103eeacbc 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt
@@ -35,7 +35,7 @@ class IdentityEditActivity : ServiceBoundSettingsActivity(IdentityEditFragment()
       context: Context,
       identity: IdentityId
     ) = Intent(context, IdentityEditActivity::class.java).apply {
-      putExtra("identity", identity)
+      putExtra("identity", identity.id)
     }
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt
index 4e0caa06c..3bbfaf988 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt
@@ -27,7 +27,7 @@ import androidx.appcompat.widget.ThemedSpinnerAdapter
 import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
-import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.util.ui.ContextThemeWrapper
@@ -55,7 +55,7 @@ class IdentityAdapter : RecyclerSpinnerAdapter<IdentityAdapter.NetworkViewHolder
     return NetworkViewHolder(inflater.inflate(R.layout.widget_spinner_item_inline, parent, false))
   }
 
-  fun indexOf(id: NetworkId): Int? {
+  fun indexOf(id: IdentityId): Int? {
     for ((key, item) in data.withIndex()) {
       if (item.id() == id) {
         return key
@@ -67,7 +67,7 @@ class IdentityAdapter : RecyclerSpinnerAdapter<IdentityAdapter.NetworkViewHolder
   override fun getItem(position: Int): Identity? =
     if (position in 0 until data.size) data[position] else null
 
-  override fun getItemId(position: Int) = getItem(position)?.id()?.toLong() ?: -1
+  override fun getItemId(position: Int) = getItem(position)?.id()?.id?.toLong() ?: -1
 
   override fun hasStableIds() = true
 
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 8c550485e..c5394955a 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
@@ -36,6 +36,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
@@ -137,7 +139,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) :
     val view = inflater.inflate(R.layout.settings_network, container, false)
     ButterKnife.bind(this, view)
 
-    val networkId = arguments?.getInt("network", -1) ?: -1
+    val networkId = NetworkId(arguments?.getInt("network", -1) ?: -1)
 
     adapter = NetworkServerAdapter(::serverClick, ::startDrag)
     servers.layoutManager = LinearLayoutManager(requireContext())
@@ -286,7 +288,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) :
   protected fun applyChanges(data: Network) {
     data.setNetworkName(networkName.text.toString())
 
-    data.setIdentity(identity.selectedItemId.toInt())
+    data.setIdentity(IdentityId(identity.selectedItemId.toInt()))
 
     data.setActualServerList(adapter.list)
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt
index 6bd392809..42b4480fd 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt
@@ -35,7 +35,7 @@ class NetworkEditActivity : ServiceBoundSettingsActivity(NetworkEditFragment())
       context: Context,
       network: NetworkId
     ) = Intent(context, NetworkEditActivity::class.java).apply {
-      putExtra("network", network)
+      putExtra("network", network.id)
     }
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt
index e23c27cf0..962f6161d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt
@@ -37,7 +37,7 @@ class ChannelInfoActivity : ServiceBoundSettingsActivity(ChannelInfoFragment())
       openBuffer: Boolean,
       bufferId: BufferId
     ) = Intent(context, ChannelInfoActivity::class.java).apply {
-      putExtra("bufferId", bufferId)
+      putExtra("bufferId", bufferId.id)
       putExtra("openBuffer", openBuffer)
     }
   }
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 3ab0bca26..4a04aae2e 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
@@ -29,7 +29,9 @@ import android.widget.TextView
 import androidx.lifecycle.Observer
 import butterknife.BindView
 import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.util.helpers.value
@@ -79,21 +81,23 @@ class ChannelInfoFragment : ServiceBoundFragment() {
     ButterKnife.bind(this, view)
 
     val openBuffer = arguments?.getBoolean("openBuffer")
+    val bufferId = BufferId(arguments?.getInt("bufferId") ?: -1)
+    val networkId = NetworkId(arguments?.getInt("networkId") ?: -1)
 
-    var currentBufferInfo: BufferInfo? = null
+    var currentBufferInfo: BufferInfo?
 
     combineLatest(viewModel.session, viewModel.networks).map { (sessionOptional, networks) ->
       if (openBuffer == true) {
         val session = sessionOptional?.orNull()
         val bufferSyncer = session?.bufferSyncer
-        val bufferInfo = bufferSyncer?.bufferInfo(arguments?.getInt("bufferId") ?: -1)
+        val bufferInfo = bufferSyncer?.bufferInfo(bufferId)
         bufferInfo?.let { info ->
           networks[info.networkId]?.ircChannel(info.bufferName)?.let {
             Pair(info, it)
           }
         }
       } else {
-        networks[arguments?.getInt("networkId")]?.ircChannel(arguments?.getString("nick"))?.let {
+        networks[networkId]?.ircChannel(arguments?.getString("nick"))?.let {
           Pair(null, it)
         }
       } ?: Pair(null, IrcChannel.NULL)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt
index 0258ce035..f786b33f4 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt
@@ -35,7 +35,7 @@ class ChannelListActivity : ServiceBoundSettingsActivity(ChannelListFragment())
       context: Context,
       network: NetworkId
     ) = Intent(context, ChannelListActivity::class.java).apply {
-      putExtra("network_id", network)
+      putExtra("network_id", network.id)
     }
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt
index 6b9d849b5..3e96dc703 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt
@@ -46,10 +46,12 @@ class ChannelListAdapter @Inject constructor(
 ) :
   ListAdapter<IrcListHelper.ChannelDescription, ChannelListAdapter.ChannelViewHolder>(
     object : DiffUtil.ItemCallback<IrcListHelper.ChannelDescription>() {
-      override fun areItemsTheSame(oldItem: IrcListHelper.ChannelDescription, newItem: IrcListHelper.ChannelDescription) =
+      override fun areItemsTheSame(oldItem: IrcListHelper.ChannelDescription,
+                                   newItem: IrcListHelper.ChannelDescription) =
         oldItem.channelName == newItem.channelName
 
-      override fun areContentsTheSame(oldItem: IrcListHelper.ChannelDescription, newItem: IrcListHelper.ChannelDescription) =
+      override fun areContentsTheSame(oldItem: IrcListHelper.ChannelDescription,
+                                      newItem: IrcListHelper.ChannelDescription) =
         oldItem == newItem
     }
   ) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt
index 8ce88f5ff..b8e1baf97 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt
@@ -109,7 +109,7 @@ class ChannelListFragment : ServiceBoundSettingsFragment() {
     val view = inflater.inflate(R.layout.info_channellist, container, false)
     ButterKnife.bind(this, view)
 
-    val networkId = arguments?.getInt("network_id", -1) ?: -1
+    val networkId = NetworkId(arguments?.getInt("network_id", -1) ?: -1)
 
     searchResults.adapter = adapter
     searchResults.layoutManager = LinearLayoutManager(view.context)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt
index 7d51299a4..98adeadae 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt
@@ -19,12 +19,13 @@
 
 package de.kuschku.quasseldroid.ui.info.user
 
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.quassel.syncables.Network
 
 data class IrcUserInfo(
-  val networkId: Int,
+  val networkId: NetworkId,
   val nick: String,
   val user: String? = null,
   val host: String? = null,
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt
index ad0237d0b..c4eb983a5 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt
@@ -44,13 +44,13 @@ class UserInfoActivity : ServiceBoundSettingsActivity(UserInfoFragment()) {
     ) = Intent(context, UserInfoActivity::class.java).apply {
       putExtra("openBuffer", openBuffer)
       if (bufferId != null) {
-        putExtra("bufferId", bufferId)
+        putExtra("bufferId", bufferId.id)
       }
       if (nick != null) {
         putExtra("nick", nick)
       }
       if (networkId != null) {
-        putExtra("networkId", networkId)
+        putExtra("networkId", networkId.id)
       }
     }
   }
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 a1def4e56..6da78c876 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
@@ -34,7 +34,9 @@ import android.widget.Toast
 import androidx.lifecycle.Observer
 import butterknife.BindView
 import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.util.Optional
@@ -130,11 +132,12 @@ class UserInfoFragment : ServiceBoundFragment() {
 
     val openBuffer = arguments?.getBoolean("openBuffer")
 
-    val networkId2 = arguments?.getInt("networkId")
-    val nickName2 = arguments?.getString("nick")
+    val bufferId = BufferId(arguments?.getInt("bufferId") ?: -1)
+    val networkId = NetworkId(arguments?.getInt("networkId") ?: -1)
+    val nickName = arguments?.getString("nick")
 
     var currentBufferInfo: BufferInfo? = null
-    var currentIrcUser: IrcUser? = null
+    var currentIrcUser: IrcUser?
 
     fun updateShortcutVisibility() {
       actionShortcut.visibleIf(currentBufferInfo != null)
@@ -172,15 +175,15 @@ class UserInfoFragment : ServiceBoundFragment() {
       if (openBuffer == true) {
         val session = sessionOptional?.orNull()
         val bufferSyncer = session?.bufferSyncer
-        val bufferInfo = bufferSyncer?.bufferInfo(arguments?.getInt("bufferId") ?: -1)
+        val bufferInfo = bufferSyncer?.bufferInfo(bufferId)
         bufferInfo?.let {
           networks[it.networkId]?.liveIrcUser(it.bufferName)?.switchMap(IrcUser::updates)?.map {
             processUser(it, bufferInfo)
           }
         }
       } else {
-        networks[networkId2]
-          ?.liveIrcUser(nickName2)
+        networks[networkId]
+          ?.liveIrcUser(nickName)
           ?.switchMap(IrcUser::updates)
           ?.map { user -> processUser(user) }
       } ?: Observable.just(IrcUser.NULL).map { user -> processUser(user) }
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 d07427e07..d813d329c 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
@@ -289,7 +289,7 @@ abstract class ServiceBoundSetupActivity :
 
   private fun checkConnection() {
     accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
 
     val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
       getBoolean(Keys.Status.reconnect, false)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt
index 90e4e0269..1c13bde36 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt
@@ -53,7 +53,7 @@ abstract class ServiceBoundSlideFragment : SlideFragment() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
     accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
 
     connection.context = context
     lifecycle.addObserver(connection)
@@ -63,7 +63,7 @@ abstract class ServiceBoundSlideFragment : SlideFragment() {
   override fun onStart() {
     super.onStart()
     accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
   }
 
   override fun onDestroy() {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
index 8c7f376ea..ba5c56ff8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
@@ -69,21 +69,21 @@ class QuasselSetupEntry : FrameLayout {
           wrapper.isPasswordVisibilityToggleEnabled = true
           field.inputType =
             InputType.TYPE_CLASS_TEXT or
-            InputType.TYPE_TEXT_VARIATION_PASSWORD
+              InputType.TYPE_TEXT_VARIATION_PASSWORD
           field.setText(data.defaultValue.value(""))
         }
         data.defaultValue.type == Type.QString &&
         data.key.contains("hostname", ignoreCase = true) -> {
           field.inputType =
             InputType.TYPE_CLASS_TEXT or
-            InputType.TYPE_TEXT_VARIATION_URI
+              InputType.TYPE_TEXT_VARIATION_URI
           field.setText(data.defaultValue.value(""))
         }
         data.defaultValue.type == Type.QString           -> {
           field.inputType =
             InputType.TYPE_CLASS_TEXT or
-            InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
-            InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+              InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
+              InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
           field.setText(data.defaultValue.value(""))
         }
         data.defaultValue.type == Type.Int               -> {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
index 1ed76e38c..48bd8417f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
@@ -25,6 +25,7 @@ import android.content.Intent
 import android.os.Bundle
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.ui.setup.ServiceBoundSetupActivity
@@ -41,15 +42,15 @@ class NetworkSetupActivity : ServiceBoundSetupActivity() {
 
   override fun onDone(data: Bundle) {
     val network = data.getSerializable("network") as? LinkNetwork
-    val networkId = data.getInt("network_id", -1)
-    val identity = data.getInt("identity", -1)
-    if (networkId != -1 || (network != null && identity != -1)) {
+    val networkId = NetworkId(data.getInt("network_id", -1))
+    val identity = IdentityId(data.getInt("identity", -1))
+    if (networkId.isValidId() || (network != null && identity.isValidId())) {
       viewModel.backend?.value?.ifPresent { backend ->
         val session = viewModel.session.value?.orNull()
         session?.apply {
           rpcHandler?.apply {
             when {
-              networkId != -1                  -> {
+              networkId.isValidId()            -> {
                 val buffer = bufferSyncer?.find(networkId = networkId,
                                                 type = Buffer_Type.of(Buffer_Type.StatusBuffer))
                 if (buffer != null) {
@@ -107,7 +108,7 @@ class NetworkSetupActivity : ServiceBoundSetupActivity() {
           bundle.putSerializable("network", network)
         }
         if (identity != null) {
-          bundle.putInt("identity", identity)
+          bundle.putInt("identity", identity.id)
         }
         if (channels != null) {
           bundle.putStringArray("channels", channels)
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 09b135725..c87f2a25a 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
@@ -32,6 +32,8 @@ import androidx.lifecycle.Observer
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.google.android.material.textfield.TextInputLayout
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
@@ -108,7 +110,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
     )
     val networkId = (network.selectedItem as? INetwork.NetworkInfo)?.networkId
     if (networkId != null) {
-      data.putInt("network_id", networkId)
+      data.putInt("network_id", networkId.id)
     } else {
       data.putSerializable(
         "network",
@@ -227,7 +229,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
       val linkNetwork = data.getSerializable("network") as? LinkNetwork
 
       val selectedNetworkId = if (data.containsKey("network_id")) {
-        data.getInt("network_id")
+        NetworkId(data.getInt("network_id"))
       } else {
         val existingNetwork = networks.firstOrNull {
           it.serverList.any {
@@ -236,10 +238,10 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
         }
         existingNetwork?.networkId
       }
-      val selectedNetworkPosition = networkAdapter.indexOf(selectedNetworkId ?: -1) ?: -1
+      val selectedNetworkPosition = networkAdapter.indexOf(selectedNetworkId ?: NetworkId(-1)) ?: -1
 
       if (!hasSetNetwork) {
-        if (selectedNetworkPosition != -1 || selectedNetworkId == -1) {
+        if (selectedNetworkPosition != -1 || selectedNetworkId?.isValidId() != true) {
           network.setSelection(selectedNetworkPosition)
           hasSetNetwork = true
         }
@@ -254,8 +256,8 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
         }
 
         if (data.containsKey("identity")) {
-          val identity = data.getInt("identity", -1)
-          if (identity != -1) {
+          val identity = IdentityId(data.getInt("identity", -1))
+          if (identity.isValidId()) {
             val position = identityAdapter.indexOf(identity)
             if (position == -1) {
               this.identity.setSelection(-1)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
index 0ac7ea43d..ed67800fa 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt
@@ -143,7 +143,7 @@ abstract class ServiceBoundActivity :
 
   protected fun checkConnection() {
     accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
 
     val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) {
       getBoolean(Keys.Status.reconnect, false)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
index 019f7a7d3..d7d848a7c 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt
@@ -53,7 +53,7 @@ abstract class ServiceBoundFragment : DaggerFragment() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
     accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
 
     connection.context = context
     lifecycle.addObserver(connection)
@@ -63,7 +63,7 @@ abstract class ServiceBoundFragment : DaggerFragment() {
   override fun onStart() {
     super.onStart()
     accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE)
-      ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
+                  ?.getLong(Keys.Status.selectedAccount, -1) ?: -1
   }
 
   override fun onDestroy() {
diff --git a/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt b/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt
index f1e4ed188..0f2f6743d 100644
--- a/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt
+++ b/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt
@@ -19,8 +19,7 @@
 
 package de.kuschku.quasseldroid.util
 
-import de.kuschku.libquassel.protocol.Message_Flag
-import de.kuschku.libquassel.protocol.Message_Type
+import de.kuschku.libquassel.protocol.*
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.util.avatars.AvatarHelper
@@ -31,13 +30,13 @@ import org.threeten.bp.Instant
 class AvatarHelperTest {
   @Test
   fun testGravatarAvatars() {
-    val message = QuasselDatabase.MessageData(
-      messageId = 1,
+    val message = QuasselDatabase.MessageData.of(
+      messageId = MsgId(1),
       time = Instant.now(),
       type = Message_Type.of(Message_Type.Plain),
       flag = Message_Flag.of(),
-      bufferId = 0,
-      networkId = 0,
+      bufferId = BufferId(0),
+      networkId = NetworkId(0),
       sender = "justJanne",
       senderPrefixes = "",
       realName = "Janne Koschinski <janne@kuschku.de>",
@@ -71,13 +70,13 @@ class AvatarHelperTest {
 
   @Test
   fun testIrcCloudAvatars() {
-    val message = QuasselDatabase.MessageData(
-      messageId = 1,
+    val message = QuasselDatabase.MessageData.of(
+      messageId = MsgId(1),
       time = Instant.now(),
       type = Message_Type.of(Message_Type.Plain),
       flag = Message_Flag.of(),
-      bufferId = 0,
-      networkId = 0,
+      bufferId = BufferId(0),
+      networkId = NetworkId(0),
       sender = "jwheare!sid2@irccloud.com",
       senderPrefixes = "",
       realName = "James Wheare",
@@ -111,13 +110,13 @@ class AvatarHelperTest {
 
   @Test
   fun testActualAvatars() {
-    val message = QuasselDatabase.MessageData(
-      messageId = 1,
+    val message = QuasselDatabase.MessageData.of(
+      messageId = MsgId(1),
       time = Instant.now(),
       type = Message_Type.of(Message_Type.Plain),
       flag = Message_Flag.of(),
-      bufferId = 0,
-      networkId = 0,
+      bufferId = BufferId(0),
+      networkId = NetworkId(0),
       sender = "jwheare!sid2@irccloud.com",
       senderPrefixes = "",
       realName = "James Wheare",
diff --git a/invokergenerator/build.gradle.kts b/invokergenerator/build.gradle.kts
index 0a03551ef..0e4ff262b 100644
--- a/invokergenerator/build.gradle.kts
+++ b/invokergenerator/build.gradle.kts
@@ -17,16 +17,16 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-import org.gradle.kotlin.dsl.DependencyHandlerScope
-import org.gradle.kotlin.dsl.dependencies
-import org.gradle.kotlin.dsl.project
-
 plugins {
-  id("java")
+  kotlin("jvm")
+  kotlin("kapt")
 }
 
 dependencies {
+  implementation(kotlin("stdlib", "1.3.20"))
   implementation(project(":invokerannotations"))
+  implementation("org.jetbrains.kotlin", "kotlin-compiler-embeddable", "1.3.20")
+  implementation("com.squareup", "kotlinpoet", "1.0.1")
   implementation("com.google.auto.service:auto-service:1.0-rc4")
-  implementation("com.squareup:javapoet:1.11.1")
+  kapt("com.google.auto.service:auto-service:1.0-rc4")
 }
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
new file mode 100644
index 000000000..132adb407
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
@@ -0,0 +1,29 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations
+
+import javax.annotation.processing.ProcessingEnvironment
+
+data class Context(
+  val processingEnv: ProcessingEnvironment,
+  val targetPath: String? = processingEnv.options["kapt.kotlin.generated"],
+  val sourcePath: String? = targetPath?.replace("build/generated/source/kaptKotlin/",
+                                                "src/") + "/java"
+) 
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
new file mode 100644
index 000000000..b5a4214cf
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
@@ -0,0 +1,76 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import javax.annotation.processing.Messager
+import javax.tools.Diagnostic
+
+
+fun Any?.toIndentString(): String {
+  val notFancy = toString()
+  return buildString(notFancy.length) {
+    var indent = 0
+    fun StringBuilder.line() {
+      appendln()
+      repeat(2 * indent) { append(' ') }
+    }
+
+    for (char in notFancy) {
+      if (char == ' ') continue
+
+      when (char) {
+        ')', ']' -> {
+          indent--
+          line()
+        }
+      }
+
+      if (char == '=') append(' ')
+      append(char)
+      if (char == '=') append(' ')
+
+      when (char) {
+        '(', '[', ',' -> {
+          if (char != ',') indent++
+          line()
+        }
+      }
+    }
+  }
+}
+
+fun splitQualifiedName(qualifiedName: String): Pair<String, String> {
+  val index = qualifiedName.lastIndexOf('.')
+  return if (index >= 0 && index + 1 < qualifiedName.length) {
+    Pair(qualifiedName.substring(0, index),
+         qualifiedName.substring(index + 1))
+  } else {
+    Pair("", qualifiedName)
+  }
+}
+
+fun Messager.printAST(element: PsiElement, indent: String = "") {
+  printMessage(Diagnostic.Kind.NOTE, "$indent$element {")
+  for (child in element.children) {
+    printAST(child, "$indent  ")
+  }
+  printMessage(Diagnostic.Kind.NOTE, "$indent}")
+}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java
deleted file mode 100644
index a1fdccdba..000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2019 Janne Koschinski
- * Copyright (c) 2019 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations;
-
-import com.google.auto.service.AutoService;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.JavaFile;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.WildcardTypeName;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.Processor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-@SuppressWarnings("WeakerAccess")
-@AutoService(Processor.class)
-@SupportedAnnotationTypes("de.kuschku.libquassel.annotations.Syncable")
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
-public class InvokerProcessor extends AbstractProcessor {
-
-  private Filer filer;
-
-  @Override
-  public synchronized void init(ProcessingEnvironment processingEnv) {
-    filer = processingEnv.getFiler();
-  }
-
-  @Override
-  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-    List<SyncableElement> syncableElements = new ArrayList<>();
-    for (Element element : roundEnv.getElementsAnnotatedWith(Syncable.class)) {
-      if (element.getKind() == ElementKind.INTERFACE) {
-        List<SlotElement> slotElements = new ArrayList<>();
-        for (Element element1 : element.getEnclosedElements()) {
-          if (element1.getKind() == ElementKind.METHOD) {
-            ExecutableElement it = (ExecutableElement) element1;
-            ExecutableType methodType = (ExecutableType) it.asType();
-
-            Slot slotAnnotation = element1.getAnnotation(Slot.class);
-            if (slotAnnotation != null) {
-              String slotName = slotAnnotation.value().isEmpty() ? it.getSimpleName().toString() : slotAnnotation.value();
-              slotElements.add(new SlotElement(it, methodType, slotName, slotAnnotation));
-            }
-          }
-        }
-
-        PackageElement packageElement = (PackageElement) element.getEnclosingElement();
-        TypeElement typeElement = (TypeElement) element;
-        Syncable annotation = typeElement.getAnnotation(Syncable.class);
-
-        syncableElements.add(new SyncableElement(packageElement, typeElement, annotation, slotElements));
-      }
-    }
-
-    try {
-      for (SyncableElement syncableElement : syncableElements) {
-        generateInvoker(syncableElement);
-      }
-    } catch (IOException e) {
-      e.printStackTrace();
-    }
-    return true;
-  }
-
-  private void generateInvoker(SyncableElement element) throws IOException {
-    String packageName = element.packageElement.getQualifiedName().toString() + ".invokers";
-    String invokerName = element.annotation.name() + "Invoker";
-
-    ClassName type = ClassName.get(packageName, invokerName);
-    ClassName wrongObjectTypeException = ClassName.get("de.kuschku.libquassel.quassel.exceptions", "WrongObjectTypeException");
-    ClassName unknownMethodException = ClassName.get("de.kuschku.libquassel.quassel.exceptions", "UnknownMethodException");
-    ClassName nonNullAnnotation = ClassName.get("androidx.annotation", "NonNull");
-
-    MethodSpec methodSpecConstructor = MethodSpec
-      .constructorBuilder()
-      .addModifiers(Modifier.PRIVATE)
-      .build();
-
-    FieldSpec fieldSpecInstance = FieldSpec
-      .builder(type, "INSTANCE", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
-      .initializer("new $T()", type)
-      .build();
-
-    MethodSpec methodSpecClassName = MethodSpec
-      .methodBuilder("getClassName")
-      .addModifiers(Modifier.PUBLIC)
-      .addAnnotation(nonNullAnnotation)
-      .addAnnotation(Override.class)
-      .returns(String.class)
-      .addStatement("return $S", element.annotation.name())
-      .build();
-
-    ParameterSpec parameterSpecOn = ParameterSpec
-      .builder(
-        Object.class,
-        "on"
-      )
-      .addAnnotation(nonNullAnnotation)
-      .build();
-
-    ParameterSpec parameterSpecMethod = ParameterSpec
-      .builder(
-        String.class,
-        "method"
-      )
-      .addAnnotation(nonNullAnnotation).build();
-
-    ParameterSpec parameterSpecParams = ParameterSpec
-      .builder(
-        ParameterizedTypeName.get(
-          ClassName.get(List.class),
-          WildcardTypeName.subtypeOf(
-            ParameterizedTypeName.get(
-              ClassName.get("de.kuschku.libquassel.protocol", "QVariant"),
-              WildcardTypeName.subtypeOf(Object.class)
-            )
-          )
-        ),
-        "params"
-      )
-      .addAnnotation(nonNullAnnotation)
-      .build();
-
-    MethodSpec.Builder invokeSpec = MethodSpec
-      .methodBuilder("invoke")
-      .addModifiers(Modifier.PUBLIC)
-      .addAnnotation(Override.class)
-      .addException(wrongObjectTypeException)
-      .addException(unknownMethodException)
-      .addParameter(parameterSpecOn)
-      .addParameter(parameterSpecMethod)
-      .addParameter(parameterSpecParams)
-      .beginControlFlow("if (on instanceof $T)", element.typeElement)
-      .addStatement("$T it = ($T) $N", element.typeElement, element.typeElement, parameterSpecOn)
-      .beginControlFlow("switch ($N)", parameterSpecMethod);
-
-    for (SlotElement slot : element.slots) {
-      invokeSpec = invokeSpec.beginControlFlow("case $S:", slot.slotName);
-      invokeSpec = invokeSpec.addCode("it.$N(\n$>", slot.element.getSimpleName());
-      for (int i = 0; i < slot.type.getParameterTypes().size(); i++) {
-        TypeMirror parameterType = slot.type.getParameterTypes().get(i);
-        boolean isLast = i + 1 == slot.type.getParameterTypes().size();
-
-        invokeSpec = invokeSpec.addCode("($T) ($T) $N.get($L).getData()", parameterType, Object.class, parameterSpecParams, i);
-        if (!isLast)
-          invokeSpec = invokeSpec.addCode(",");
-        invokeSpec = invokeSpec.addCode("\n");
-      }
-      invokeSpec = invokeSpec.addCode("$<);\n");
-      invokeSpec = invokeSpec.endControlFlow("return");
-    }
-
-    invokeSpec = invokeSpec
-      .beginControlFlow("default:")
-      .addStatement("throw new $T($N(), $N)",
-        unknownMethodException,
-        methodSpecClassName,
-        parameterSpecMethod
-      )
-      .endControlFlow()
-      .endControlFlow()
-      .addCode("$<} else{\n$>")
-      .addStatement("throw new $T($N, $N())",
-        wrongObjectTypeException,
-        parameterSpecOn,
-        methodSpecClassName
-      )
-      .endControlFlow();
-
-    TypeSpec typeSpec = TypeSpec
-      .classBuilder(type)
-      .addSuperinterface(ParameterizedTypeName.get(
-        ClassName.get(packageName, "Invoker"),
-        TypeName.get(element.typeElement.asType())
-      ))
-      .addModifiers(Modifier.PUBLIC)
-      .addField(fieldSpecInstance)
-      .addMethod(methodSpecConstructor)
-      .addMethod(methodSpecClassName)
-      .addMethod(invokeSpec.build())
-      .build();
-
-    JavaFile javaFile = JavaFile
-      .builder(packageName, typeSpec)
-      .build();
-
-    javaFile.writeTo(filer);
-  }
-
-  private class SlotElement {
-    final ExecutableElement element;
-    final ExecutableType type;
-
-    final String slotName;
-
-    final Slot slot;
-
-    public SlotElement(ExecutableElement element, ExecutableType type, String slotName, Slot slot) {
-      this.element = element;
-      this.type = type;
-      this.slotName = slotName;
-      this.slot = slot;
-    }
-  }
-
-  private class SyncableElement {
-    final PackageElement packageElement;
-    final TypeElement typeElement;
-
-    final Syncable annotation;
-
-    final List<SlotElement> slots;
-
-    public SyncableElement(PackageElement packageElement, TypeElement typeElement, Syncable annotation, List<SlotElement> slots) {
-      this.packageElement = packageElement;
-      this.typeElement = typeElement;
-      this.annotation = annotation;
-      this.slots = slots;
-    }
-  }
-}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
new file mode 100644
index 000000000..cb23c99c2
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations
+
+import com.google.auto.service.AutoService
+import de.kuschku.libquassel.annotations.generator.Generator
+import de.kuschku.libquassel.annotations.parser.ParserEnvironment
+import javax.annotation.processing.*
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.TypeElement
+
+@AutoService(Processor::class)
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+@SupportedAnnotationTypes("de.kuschku.libquassel.annotations.Syncable")
+class InvokerProcessor : AbstractProcessor() {
+  lateinit var parserEnvironment: ParserEnvironment
+  lateinit var generator: Generator
+
+  @Synchronized
+  override fun init(processingEnv: ProcessingEnvironment) {
+    val context = Context(processingEnv)
+    parserEnvironment = ParserEnvironment(context)
+    generator = Generator(context)
+  }
+
+  override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
+    parserEnvironment.use { parser ->
+      for (annotatedElement in roundEnv.getElementsAnnotatedWith(Syncable::class.java)) {
+        val parsedClass = parser.parse(annotatedElement)
+        if (parsedClass != null) {
+          generator.generate(parsedClass)
+        }
+      }
+    }
+    return true
+  }
+}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
new file mode 100644
index 000000000..31069e582
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
@@ -0,0 +1,28 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.data
+
+import com.squareup.kotlinpoet.ClassName
+
+data class ParsedClass(
+  val name: ClassName,
+  val quasselName: String,
+  val methods: List<ParsedMethod>
+)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
new file mode 100644
index 000000000..e2fa1ab2d
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
@@ -0,0 +1,26 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.data
+
+data class ParsedMethod(
+  val name: String?,
+  val quasselName: String,
+  val parameters: List<ParsedParameter>
+)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
new file mode 100644
index 000000000..a8211f535
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
@@ -0,0 +1,27 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.data
+
+import com.squareup.kotlinpoet.TypeName
+
+data class ParsedParameter(
+  val name: String,
+  val type: TypeName
+)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
new file mode 100644
index 000000000..fd95ac366
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
@@ -0,0 +1,112 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.generator
+
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import de.kuschku.libquassel.annotations.Context
+import de.kuschku.libquassel.annotations.data.ParsedClass
+import java.io.File
+
+class Generator(
+  private val context: Context
+) {
+  fun generate(parsedClass: ParsedClass) {
+    val file = FileSpec.builder(
+      parsedClass.name.packageName + ".invokers",
+      parsedClass.quasselName + "Invoker"
+    ).addType(
+      TypeSpec.objectBuilder(parsedClass.quasselName + "Invoker")
+        .addSuperinterface(TYPENAME_INVOKER.parameterizedBy(parsedClass.name))
+        .addProperty(
+          PropertySpec.builder(
+            "className",
+            String::class.asTypeName(),
+            KModifier.OVERRIDE
+          ).initializer("\"${parsedClass.quasselName}\"").build()
+        )
+        .addFunction(
+          FunSpec.builder("invoke")
+            .addModifiers(KModifier.OVERRIDE, KModifier.OPERATOR)
+            .addParameter(
+              ParameterSpec.builder(
+                "on",
+                ANY.copy(nullable = true)
+              ).build()
+            ).addParameter(
+              ParameterSpec.builder(
+                "method",
+                String::class.asTypeName()
+              ).build()
+            ).addParameter(
+              ParameterSpec.builder(
+                "params",
+                TYPENAME_QVARIANTLIST
+              ).build()
+            )
+            .addCode(
+              buildCodeBlock {
+                beginControlFlow("if (on is %T)", parsedClass.name)
+                beginControlFlow("when (method)")
+                for (method in parsedClass.methods) {
+                  beginControlFlow("%S ->", method.quasselName)
+                  addStatement("on.${method.name}(")
+                  indent()
+                  val lastIndex = method.parameters.size - 1
+                  for ((i, parameter) in method.parameters.withIndex()) {
+                    if (i == lastIndex) {
+                      addStatement("${parameter.name} = params[$i].data as %T", parameter.type)
+                    } else {
+                      addStatement("${parameter.name} = params[$i].data as %T,", parameter.type)
+                    }
+                  }
+                  unindent()
+                  addStatement(")")
+                  endControlFlow()
+                }
+                endControlFlow()
+                nextControlFlow("else")
+                addStatement("throw %T(on, className)", TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION)
+                endControlFlow()
+              }
+            )
+            .build()
+        )
+        .build()
+    ).build()
+
+    file.writeTo(File(context.targetPath))
+  }
+
+  companion object {
+    private val TYPENAME_INVOKER = ClassName(
+      "de.kuschku.libquassel.quassel.syncables.interfaces.invokers",
+      "Invoker"
+    )
+    private val TYPENAME_QVARIANTLIST = ClassName(
+      "de.kuschku.libquassel.protocol",
+      "QVariantList"
+    )
+    private val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName(
+      "de.kuschku.libquassel.quassel.exceptions",
+      "WrongObjectTypeException"
+    )
+  }
+}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
new file mode 100644
index 000000000..e25bb6dde
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
@@ -0,0 +1,255 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.parser
+
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.asClassName
+import de.kuschku.libquassel.annotations.Context
+import de.kuschku.libquassel.annotations.Slot
+import de.kuschku.libquassel.annotations.Syncable
+import de.kuschku.libquassel.annotations.data.ParsedClass
+import de.kuschku.libquassel.annotations.data.ParsedMethod
+import de.kuschku.libquassel.annotations.data.ParsedParameter
+import de.kuschku.libquassel.annotations.splitQualifiedName
+import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory
+import org.jetbrains.kotlin.idea.KotlinLanguage
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
+import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
+import java.io.File
+import java.net.JarURLConnection
+import javax.lang.model.element.Element
+import javax.lang.model.element.TypeElement
+
+class Parser(
+  private val context: Context,
+  private val environment: KotlinCoreEnvironment
+) {
+  fun parse(element: Element): ParsedClass? {
+    val typeElement = element as TypeElement
+
+    val sourcePath = typeElement.qualifiedName.toString().replace('.', '/') + ".kt"
+    val sourceFile = File(context.sourcePath, sourcePath)
+
+    val source = sourceFile.readText(Charsets.UTF_8)
+
+    val file = PsiFileFactory.getInstance(environment.project)
+      .createFileFromText(KotlinLanguage.INSTANCE, source)
+
+    val importDirectives = file.collectDescendantsOfType<KtImportDirective>()
+    val imports = buildImports(
+      wildcard = importDirectives.filter {
+        it.importedName == null
+      }.mapNotNull {
+        it.importPath?.toString()
+      },
+      named = importDirectives.mapNotNull {
+        val simpleName = it.alias?.toString() ?: it.importedName?.toString()
+        val qualifiedName = it.importPath?.toString()
+        if (simpleName != null && qualifiedName != null) {
+          Pair(simpleName, qualifiedName)
+        } else {
+          null
+        }
+      }
+    )
+
+    val clazz = file.collectDescendantsOfType<KtClass> {
+      it.isInterface() && it.annotationEntries.any {
+        it.shortName.toString() == Syncable::class.java.simpleName
+      }
+    }.first()
+    val body = clazz.findDescendantOfType<KtClassBody>()
+
+    if (body != null) {
+      val methods = body.collectDescendantsOfType<KtFunction> {
+        it.annotationEntries.any {
+          it.shortName.toString() == Slot::class.java.simpleName
+        }
+      }
+
+      val subclassImports = body.children.mapNotNull {
+        it as? KtClass
+      }.map {
+        Pair(it.name!!, typeElement.asClassName().canonicalName + "." + it.name)
+      }.toMap()
+      val importsWithSubclasses = imports + subclassImports
+
+      return ParsedClass(
+        name = typeElement.asClassName(),
+        quasselName = clazz.parseAnnotations<Syncable>()["name"]
+                      ?: clazz.name
+                      ?: "",
+        methods = methods.map { method ->
+          parseMethod(method, importsWithSubclasses)
+        }
+      )
+    }
+    return null
+  }
+
+  private fun parseMethod(method: KtFunction, imports: Map<String, String>) = ParsedMethod(
+    name = method.name
+           ?: "",
+    quasselName = method.parseAnnotations<Slot>()["value"]
+                  ?: method.name
+                  ?: "",
+    parameters = method
+      .findDescendantOfType<KtParameterList>()
+      ?.collectDescendantsOfType<KtParameter>()
+      .orEmpty()
+      .map {
+        parseParameter(it, imports)
+      }
+  )
+
+  private fun parseTypeReference(typeReference: KtTypeReference?,
+                                 imports: Map<String, String>): TypeName {
+    val child = typeReference?.firstChild
+    return when (child) {
+             is KtUserType     -> parseUserType(child, imports)
+             is KtNullableType -> parseNullableType(child, imports)
+             else              -> throw IllegalArgumentException("Invalid Type")
+           } ?: throw IllegalArgumentException("Invalid Type")
+  }
+
+  private fun parseUserType(type: KtUserType, imports: Map<String, String>): TypeName? {
+    val qualifiedName = resolveImport(imports, type.referencedName.toString())
+    val typeArguments = type.children.mapNotNull {
+      it as? KtTypeArgumentList
+    }.firstOrNull()?.children.orEmpty().mapNotNull {
+      it as? KtTypeProjection
+    }.mapNotNull {
+      it.typeReference
+    }.map {
+      parseTypeReference(it, imports)
+    }.toTypedArray()
+    val (packageName, className) = splitQualifiedName(qualifiedName)
+    val typeName = ClassName(packageName, className)
+    return if (typeArguments.isEmpty()) {
+      typeName
+    } else {
+      typeName.parameterizedBy(*typeArguments)
+    }
+  }
+
+  private fun parseNullableType(type: KtNullableType, imports: Map<String, String>) =
+    type.findDescendantOfType<KtUserType>()?.let {
+      parseUserType(it, imports)
+    }?.copy(nullable = true)
+
+  private fun parseParameter(parameter: KtParameter, imports: Map<String, String>) =
+    ParsedParameter(
+      parameter.name!!,
+      parseTypeReference(parameter.findDescendantOfType(), imports)
+    )
+
+  private inline fun <reified T> KtAnnotated.parseAnnotations(): Map<String, String?> =
+    annotationEntries
+      .first {
+        it.shortName.toString() == T::class.java.simpleName
+      }
+      .findDescendantOfType<KtValueArgumentList>()
+      ?.collectDescendantsOfType<KtValueArgument>()
+      .orEmpty()
+      .map {
+        Pair(
+          it.findDescendantOfType<KtValueArgumentName>()
+            ?.findDescendantOfType<KtReferenceExpression>()
+            ?.text
+          ?: "value",
+          it.findDescendantOfType<KtLiteralStringTemplateEntry>()?.text
+        )
+      }
+      .toMap()
+
+  private fun resolveImport(imports: Map<String, String>, import: String): String {
+    val qualifiedName = imports.getOrDefault(import, import)
+    return JavaToKotlinClassMap.mapJavaToKotlin(FqName(qualifiedName))?.asSingleFqName()?.asString()
+           ?: qualifiedName
+  }
+
+  private fun resolveWildcardImport(import: String): List<Pair<String, String>> {
+    val imports = mutableListOf<Pair<String, String>>()
+
+    val packageName = import.removeSuffix(".*")
+    val packagePath = packageName.replace('.', '/')
+    val folder = File(context.sourcePath, packagePath)
+    val sourceFiles = folder.listFiles()?.filter { it.isFile }
+    if (sourceFiles == null) {
+      val jarURLConnection = Object::class.java.getResource("Object.class").openConnection() as JarURLConnection
+      val jdkFile = jarURLConnection.jarFile
+      for (classEntry in jdkFile.entries()) {
+        if (classEntry.name.endsWith(".class") &&
+            classEntry.name.startsWith(packagePath) &&
+            !classEntry.name.contains('$')) {
+          val qualifiedName = classEntry.name.removeSuffix(".class").replace('/', '.')
+          val (_, simpleName) = splitQualifiedName(qualifiedName)
+          imports.add(Pair(simpleName, qualifiedName))
+        }
+      }
+    } else {
+      for (sourceFile in sourceFiles) {
+        val source = sourceFile.readText(Charsets.UTF_8)
+        val file = PsiFileFactory.getInstance(environment.project)
+          .createFileFromText(KotlinLanguage.INSTANCE, source)
+
+        val foundPackageName = file.findDescendantOfType<KtPackageDirective>()
+          ?.qualifiedName
+
+        val classes = file.findDescendantOfType<KtScript>()?.children?.mapNotNull {
+          it as? KtBlockExpression
+        }.orEmpty().flatMap { it.children.asIterable() }.mapNotNull {
+          it as? KtClass
+        }
+        for (clazz in classes) {
+          val className = clazz.name
+          if (className != null) {
+            imports.add(
+              Pair(
+                className,
+                listOfNotNull(foundPackageName, className).joinToString(".")
+              )
+            )
+          }
+        }
+
+        val typeAliases = file.collectDescendantsOfType<KtTypeAlias>()
+        for (typeAlias in typeAliases) {
+          val className = typeAlias.name
+          if (className != null) {
+            imports.add(Pair(
+              className,
+              listOfNotNull(foundPackageName, className).joinToString(".")))
+          }
+        }
+      }
+    }
+    return imports
+  }
+
+  private fun buildImports(wildcard: List<String>, named: List<Pair<String, String>>) =
+    (wildcard.flatMap(this::resolveWildcardImport) + named).toMap()
+}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
new file mode 100644
index 000000000..3d83a30ec
--- /dev/null
+++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
@@ -0,0 +1,52 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.annotations.parser
+
+import de.kuschku.libquassel.annotations.Context
+import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
+import org.jetbrains.kotlin.config.CompilerConfiguration
+
+class ParserEnvironment(
+  private val context: Context
+) {
+  fun <T> use(f: (Parser) -> T): T {
+    val rootDisposable = Disposer.newDisposable()
+    try {
+      val environment = KotlinCoreEnvironment.createForProduction(
+        rootDisposable,
+        CompilerConfiguration().apply {
+          context.sourcePath?.let {
+            addKotlinSourceRoot(it, isCommon = false)
+          }
+        },
+        EnvironmentConfigFiles.JVM_CONFIG_FILES
+      )
+
+      val parser = Parser(context, environment)
+
+      return f(parser)
+    } finally {
+      rootDisposable.dispose()
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt
index 977970a12..3ae8faa1b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt
@@ -23,7 +23,7 @@ import de.kuschku.libquassel.protocol.primitive.serializer.*
 
 enum class QType(val typeName: String, val serializer: Serializer<*>,
                  val type: Type = Type.UserType) {
-  BufferId("BufferId", IntSerializer),
+  BufferId("BufferId", BufferIdSerializer),
   BufferInfo("BufferInfo", BufferInfoSerializer),
   DccConfig_IpDetectionMode("DccConfig::IpDetectionMode",
                             DccConfig_IpDetectionModeSerializer),
@@ -32,10 +32,10 @@ enum class QType(val typeName: String, val serializer: Serializer<*>,
   IrcUser("IrcUser", VariantMapSerializer),
   IrcChannel("IrcChannel", VariantMapSerializer),
   Identity("Identity", VariantMapSerializer),
-  IdentityId("IdentityId", IntSerializer),
+  IdentityId("IdentityId", IdentityIdSerializer),
   Message("Message", MessageSerializer),
-  MsgId("MsgId", SignedId64Serializer),
-  NetworkId("NetworkId", IntSerializer),
+  MsgId("MsgId", MsgIdSerializer),
+  NetworkId("NetworkId", NetworkIdSerializer),
   NetworkInfo("NetworkInfo", VariantMapSerializer),
   Network_Server("Network::Server", VariantMapSerializer),
   QHostAddress("QHostAddress", HostAddressSerializer),
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
index 79ac7d4c6..eb5ad4338 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
@@ -35,14 +35,6 @@ typealias QVariant_ = QVariant<*>
 typealias QVariantMap = Map<String, QVariant_>
 typealias QVariantList = List<QVariant_>
 
-typealias SignedId = Int
-typealias SignedId64 = Long
-
-typealias IdentityId = SignedId
-typealias BufferId = SignedId
-typealias MsgId = SignedId64
-typealias NetworkId = SignedId
-
 typealias Message_Type = Message.MessageType
 typealias Message_Types = Flags<Message_Type>
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt
new file mode 100644
index 000000000..1167b82dd
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt
@@ -0,0 +1,87 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol
+
+typealias SignedId = Int
+typealias SignedId64 = Long
+
+typealias MsgId_Type = SignedId64
+
+inline class MsgId(val id: MsgId_Type) : Comparable<MsgId> {
+  override fun compareTo(other: MsgId) = id.compareTo(other.id)
+  inline fun isValidId() = id > 0
+
+  override fun toString(): String {
+    return "MsgId($id)"
+  }
+
+  companion object {
+    val MIN_VALUE = MsgId(MsgId_Type.MIN_VALUE)
+    val MAX_VALUE = MsgId(MsgId_Type.MAX_VALUE)
+  }
+}
+
+typealias NetworkId_Type = SignedId
+
+inline class NetworkId(val id: NetworkId_Type) : Comparable<NetworkId> {
+  override fun compareTo(other: NetworkId) = id.compareTo(other.id)
+  inline fun isValidId() = id > 0
+
+  override fun toString(): String {
+    return "NetworkId($id)"
+  }
+
+  companion object {
+    val MIN_VALUE = NetworkId(NetworkId_Type.MIN_VALUE)
+    val MAX_VALUE = NetworkId(NetworkId_Type.MAX_VALUE)
+  }
+}
+
+typealias BufferId_Type = SignedId
+
+inline class BufferId(val id: BufferId_Type) : Comparable<BufferId> {
+  override fun compareTo(other: BufferId) = id.compareTo(other.id)
+  inline fun isValidId() = id > 0
+
+  override fun toString(): String {
+    return "BufferId($id)"
+  }
+
+  companion object {
+    val MIN_VALUE = BufferId(BufferId_Type.MIN_VALUE)
+    val MAX_VALUE = BufferId(BufferId_Type.MAX_VALUE)
+  }
+}
+
+typealias IdentityId_Type = SignedId
+
+inline class IdentityId(val id: IdentityId_Type) : Comparable<IdentityId> {
+  override fun compareTo(other: IdentityId) = id.compareTo(other.id)
+  inline fun isValidId() = id > 0
+
+  override fun toString(): String {
+    return "IdentityId($id)"
+  }
+
+  companion object {
+    val MIN_VALUE = IdentityId(IdentityId_Type.MIN_VALUE)
+    val MAX_VALUE = IdentityId(IdentityId_Type.MAX_VALUE)
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt
new file mode 100644
index 000000000..96754d435
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.primitive.serializer
+
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import de.kuschku.libquassel.util.nio.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+object BufferIdSerializer : Serializer<BufferId> {
+  override fun serialize(buffer: ChainedByteBuffer, data: BufferId, features: QuasselFeatures) {
+    SignedIdSerializer.serialize(buffer, data.id, features)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): BufferId {
+    return BufferId(SignedIdSerializer.deserialize(buffer, features))
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt
index ab9da5837..11d4758d1 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt
@@ -27,16 +27,16 @@ import java.nio.ByteBuffer
 
 object BufferInfoSerializer : Serializer<BufferInfo> {
   override fun serialize(buffer: ChainedByteBuffer, data: BufferInfo, features: QuasselFeatures) {
-    IntSerializer.serialize(buffer, data.bufferId, features)
-    IntSerializer.serialize(buffer, data.networkId, features)
+    BufferIdSerializer.serialize(buffer, data.bufferId, features)
+    NetworkIdSerializer.serialize(buffer, data.networkId, features)
     ShortSerializer.serialize(buffer, data.type.toShort(), features)
     IntSerializer.serialize(buffer, data.groupId, features)
     StringSerializer.UTF8.serialize(buffer, data.bufferName, features)
   }
 
   override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): BufferInfo {
-    val bufferId = IntSerializer.deserialize(buffer, features)
-    val networkId = IntSerializer.deserialize(buffer, features)
+    val bufferId = BufferIdSerializer.deserialize(buffer, features)
+    val networkId = NetworkIdSerializer.deserialize(buffer, features)
     val type = Buffer_Type.of(ShortSerializer.deserialize(buffer, features))
     val groupId = IntSerializer.deserialize(buffer, features)
     val bufferName = StringSerializer.UTF8.deserialize(buffer, features)
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt
new file mode 100644
index 000000000..7283fe893
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.primitive.serializer
+
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import de.kuschku.libquassel.util.nio.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+object IdentityIdSerializer : Serializer<IdentityId> {
+  override fun serialize(buffer: ChainedByteBuffer, data: IdentityId, features: QuasselFeatures) {
+    SignedIdSerializer.serialize(buffer, data.id, features)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): IdentityId {
+    return IdentityId(SignedIdSerializer.deserialize(buffer, features))
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
index 4d1aa2a33..e3d038887 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt
@@ -28,7 +28,7 @@ import java.nio.ByteBuffer
 
 object MessageSerializer : Serializer<Message> {
   override fun serialize(buffer: ChainedByteBuffer, data: Message, features: QuasselFeatures) {
-    SignedId64Serializer.serialize(buffer, data.messageId, features)
+    MsgIdSerializer.serialize(buffer, data.messageId, features)
     if (features.hasFeature(ExtendedFeature.LongTime))
       LongSerializer.serialize(buffer, data.time.toEpochMilli(), features)
     else
@@ -48,7 +48,7 @@ object MessageSerializer : Serializer<Message> {
 
   override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): Message {
     return Message(
-      messageId = SignedId64Serializer.deserialize(buffer, features),
+      messageId = MsgIdSerializer.deserialize(buffer, features),
       time = if (features.hasFeature(ExtendedFeature.LongTime))
         Instant.ofEpochMilli(LongSerializer.deserialize(buffer, features))
       else
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt
new file mode 100644
index 000000000..818f93066
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.primitive.serializer
+
+import de.kuschku.libquassel.protocol.MsgId
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import de.kuschku.libquassel.util.nio.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+object MsgIdSerializer : Serializer<MsgId> {
+  override fun serialize(buffer: ChainedByteBuffer, data: MsgId, features: QuasselFeatures) {
+    SignedId64Serializer.serialize(buffer, data.id, features)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): MsgId {
+    return MsgId(SignedId64Serializer.deserialize(buffer, features))
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt
new file mode 100644
index 000000000..5adc15ebc
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.primitive.serializer
+
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import de.kuschku.libquassel.util.nio.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+object NetworkIdSerializer : Serializer<NetworkId> {
+  override fun serialize(buffer: ChainedByteBuffer, data: NetworkId, features: QuasselFeatures) {
+    SignedIdSerializer.serialize(buffer, data.id, features)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): NetworkId {
+    return NetworkId(SignedIdSerializer.deserialize(buffer, features))
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt
new file mode 100644
index 000000000..a9faab417
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.primitive.serializer
+
+import de.kuschku.libquassel.protocol.SignedId
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import de.kuschku.libquassel.util.nio.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+object SignedIdSerializer : Serializer<SignedId> {
+  override fun serialize(buffer: ChainedByteBuffer, data: SignedId, features: QuasselFeatures) {
+    buffer.putInt(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): SignedId {
+    return buffer.int
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
index 3e7510584..e70453469 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
@@ -19,16 +19,18 @@
 
 package de.kuschku.libquassel.quassel
 
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.Buffer_Types
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.util.flag.Flag
 import de.kuschku.libquassel.util.flag.Flags
 import de.kuschku.libquassel.util.flag.ShortFlag
 import de.kuschku.libquassel.util.flag.ShortFlags
 
 data class BufferInfo(
-  var bufferId: Int = -1,
-  var networkId: Int = -1,
+  var bufferId: BufferId = BufferId(-1),
+  var networkId: NetworkId = NetworkId(-1),
   var type: Buffer_Types = Buffer_Type.of(),
   var groupId: Int = -1,
   var bufferName: String? = null
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
index 369d28711..31188fed8 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt
@@ -44,33 +44,34 @@ class BacklogManager(
 
   fun updateIgnoreRules() = backlogStorage?.updateIgnoreRules(session)
 
-  fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
-                     additional: Int = 0, callback: (List<Message>) -> Boolean) {
+  fun requestBacklog(bufferId: BufferId, first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
+                     limit: Int = -1, additional: Int = 0, callback: (List<Message>) -> Boolean) {
     if (loading.contains(bufferId)) return
     loading[bufferId] = callback
     requestBacklog(bufferId, first, last, limit, additional)
   }
 
-  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1,
-                             limit: Int = -1, additional: Int = 0, type: Int = -1, flags: Int = -1,
+  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = MsgId(-1),
+                             last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0,
+                             type: Int = -1, flags: Int = -1,
                              callback: (List<Message>) -> Boolean) {
     if (loadingFiltered.contains(bufferId)) return
     loadingFiltered[bufferId] = callback
     requestBacklogFiltered(bufferId, first, last, limit, additional, type, flags)
   }
 
-  fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, additional: Int = 0,
-                        callback: (List<Message>) -> Boolean) {
-    if (loading.contains(-1)) return
-    loading[-1] = callback
+  fun requestBacklogAll(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1,
+                        additional: Int = 0, callback: (List<Message>) -> Boolean) {
+    if (loading.contains(BufferId(-1))) return
+    loading[BufferId(-1)] = callback
     requestBacklogAll(first, last, limit, additional)
   }
 
-  fun requestBacklogAllFiltered(first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
-                                additional: Int = 0, type: Int = -1, flags: Int = -1,
-                                callback: (List<Message>) -> Boolean) {
-    if (loading.contains(-1)) return
-    loadingFiltered[-1] = callback
+  fun requestBacklogAllFiltered(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
+                                limit: Int = -1, additional: Int = 0, type: Int = -1,
+                                flags: Int = -1, callback: (List<Message>) -> Boolean) {
+    if (loadingFiltered.contains(BufferId(-1))) return
+    loadingFiltered[BufferId(-1)] = callback
     requestBacklogAllFiltered(first, last, limit, additional, type, flags)
   }
 
@@ -86,7 +87,7 @@ class BacklogManager(
   override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int,
                                  messages: QVariantList) {
     val list = messages.mapNotNull<QVariant_, Message>(QVariant_::value)
-    if (loading.remove(-1)?.invoke(list) != false) {
+    if (loading.remove(BufferId(-1))?.invoke(list) != false) {
       log(DEBUG, "BacklogManager", "storeMessages(${list.size})")
       backlogStorage?.storeMessages(session, list)
     }
@@ -105,7 +106,7 @@ class BacklogManager(
   override fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int,
                                          type: Int, flags: Int, messages: QVariantList) {
     val list = messages.mapNotNull<QVariant_, Message>(QVariant_::value)
-    if (loadingFiltered.remove(-1)?.invoke(list) != false) {
+    if (loadingFiltered.remove(BufferId(-1))?.invoke(list) != false) {
       log(DEBUG, "BacklogManager", "storeMessages(${list.size})")
       backlogStorage?.storeMessages(session, list)
     }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
index fbc7df8cb..6a1c9e48f 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
@@ -39,7 +39,7 @@ class BufferSyncer constructor(
     session = ISession.NULL
   }
 
-  fun lastSeenMsg(buffer: BufferId): MsgId = _lastSeenMsg[buffer] ?: 0
+  fun lastSeenMsg(buffer: BufferId): MsgId = _lastSeenMsg[buffer] ?: MsgId(0)
   fun liveLastSeenMsg(buffer: BufferId): Observable<MsgId> = live_lastSeenMsg.map {
     markerLine(buffer)
   }.distinctUntilChanged()
@@ -47,7 +47,7 @@ class BufferSyncer constructor(
   fun liveLastSeenMsgs(): Observable<Map<BufferId, MsgId>> =
     live_lastSeenMsg.map { _lastSeenMsg.toMap() }
 
-  fun markerLine(buffer: BufferId): MsgId = _markerLines[buffer] ?: 0
+  fun markerLine(buffer: BufferId): MsgId = _markerLines[buffer] ?: MsgId(0)
   fun liveMarkerLine(buffer: BufferId): Observable<MsgId> =
     live_markerLines.map { markerLine(buffer) }.distinctUntilChanged()
 
@@ -129,7 +129,7 @@ class BufferSyncer constructor(
 
   override fun initSetActivities(data: QVariantList) {
     (0 until data.size step 2).map {
-      data[it].value(0) to data[it + 1].value(0)
+      data[it].value(BufferId(0)) to data[it + 1].value(0)
     }.forEach { (buffer, activity) ->
       setBufferActivity(buffer, activity)
     }
@@ -138,7 +138,7 @@ class BufferSyncer constructor(
 
   override fun initSetHighlightCounts(data: QVariantList) {
     (0 until data.size step 2).map {
-      data[it].value(0) to data[it + 1].value(0)
+      data[it].value(BufferId(0)) to data[it + 1].value(0)
     }.forEach { (buffer, count) ->
       setHighlightCount(buffer, count)
     }
@@ -147,7 +147,7 @@ class BufferSyncer constructor(
 
   override fun initSetLastSeenMsg(data: QVariantList) {
     (0 until data.size step 2).map {
-      data[it].value(0) to data[it + 1].value(0L)
+      data[it].value(BufferId(0)) to data[it + 1].value(MsgId(0L))
     }.forEach { (buffer, msgId) ->
       setLastSeenMsg(buffer, msgId)
     }
@@ -156,7 +156,7 @@ class BufferSyncer constructor(
 
   override fun initSetMarkerLines(data: QVariantList) {
     (0 until data.size step 2).map {
-      data[it].value(0) to data[it + 1].value(0L)
+      data[it].value(BufferId(0)) to data[it + 1].value(MsgId(0L))
     }.forEach { (buffer, msgId) ->
       setMarkerLine(buffer, msgId)
     }
@@ -204,7 +204,7 @@ class BufferSyncer constructor(
   }
 
   override fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) {
-    if (msgId < 0)
+    if (msgId < MsgId(0))
       return
 
     val oldLastSeenMsg = lastSeenMsg(buffer)
@@ -217,7 +217,7 @@ class BufferSyncer constructor(
   }
 
   override fun setMarkerLine(buffer: BufferId, msgId: MsgId) {
-    if (msgId < 0 || markerLine(buffer) == msgId)
+    if (msgId < MsgId(0) || markerLine(buffer) == msgId)
       return
 
     _markerLines[buffer] = msgId
@@ -260,7 +260,7 @@ class BufferSyncer constructor(
   }.filter {
     groupId == null || it.groupId == groupId
   }.filter {
-    val caseMapper = IrcCaseMappers[session.networks[it.bufferId]?.support("CASEMAPPING")]
+    val caseMapper = IrcCaseMappers[session.networks[it.networkId]?.support("CASEMAPPING")]
     bufferName == null || caseMapper.equalsIgnoreCaseNullable(it.bufferName, bufferName)
   }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
index 59a3669d7..22ebb65ef 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
@@ -130,12 +130,12 @@ class BufferViewConfig constructor(
 
     if (currentPos > targetPos) {
       _buffers.removeAt(currentPos)
-      _buffers.add(bufferId, targetPos)
+      _buffers.add(targetPos, bufferId)
     }
 
     if (currentPos < targetPos) {
       _buffers.removeAt(currentPos)
-      _buffers.add(bufferId, targetPos - 1)
+      _buffers.add(targetPos - 1, bufferId)
     }
 
     live_buffers.onNext(Unit)
@@ -267,7 +267,7 @@ class BufferViewConfig constructor(
       field = value
       live_config.onNext(Unit)
     }
-  private var _networkId: NetworkId = 0
+  private var _networkId: NetworkId = NetworkId(0)
     set(value) {
       field = value
       live_config.onNext(Unit)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
index fdf6bc6d7..948e28b47 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
@@ -29,7 +29,7 @@ class CertManager constructor(
   proxy: SignalProxy
 ) : SyncableObject(proxy, "CertManager"), ICertManager {
   override fun init() {
-    renameObject("$_identityId")
+    renameObject("${_identityId.id}")
   }
 
   override fun toVariantMap() = initProperties()
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
index e036e1d77..0b50a9040 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
@@ -34,7 +34,7 @@ class Identity constructor(
   }
 
   override fun init() {
-    renameObject("${id()}")
+    renameObject("${id().id}")
   }
 
   override fun initProperties(): QVariantMap = mapOf(
@@ -210,7 +210,7 @@ class Identity constructor(
 
   private val _change = BehaviorSubject.createDefault(Unit)
 
-  private var _identityId: IdentityId = -1
+  private var _identityId: IdentityId = IdentityId(-1)
     set(value) {
       field = value
       _change.onNext(Unit)
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 b97056c4e..6819fff92 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
@@ -40,7 +40,7 @@ class IrcChannel(
     if (name().isEmpty()) {
       log(ERROR, "IrcChannel", "Error: channelName is empty")
     }
-    renameObject("${network().networkId()}/${name()}")
+    renameObject("${network().networkId().id}/${name()}")
   }
 
   override fun toVariantMap(): QVariantMap = mapOf(
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt
index e067c96bb..0ffd69501 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt
@@ -30,7 +30,7 @@ import de.kuschku.libquassel.util.rxjava.ReusableUnicastSubject
 class IrcListHelper constructor(
   proxy: SignalProxy
 ) : SyncableObject(proxy, "IrcListHelper"), IIrcListHelper {
-  private var waitingNetwork: NetworkId = 0
+  private var waitingNetwork: NetworkId = NetworkId(0)
 
   data class ChannelDescription(
     val netId: NetworkId,
@@ -75,7 +75,7 @@ class IrcListHelper constructor(
 
   override fun reportFinishedList(netId: NetworkId) {
     if (waitingNetwork == netId) {
-      waitingNetwork = 0
+      waitingNetwork = NetworkId(0)
       requestChannelList(netId, emptyList())
       subject.onNext(Event.Finished(netId))
     }
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 2ca56f3ac..cf21fd1fc 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
@@ -283,7 +283,7 @@ class IrcUser(
   }
 
   fun updateObjectName() {
-    val identifier = "${network().networkId()}/${nick()}"
+    val identifier = "${network().networkId().id}/${nick()}"
     renameObject(identifier)
   }
 
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 d0ef17881..77e28b4b0 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
@@ -39,7 +39,7 @@ class Network constructor(
   proxy: SignalProxy
 ) : SyncableObject(proxy, "Network"), INetwork {
   override fun init() {
-    renameObject("$_networkId")
+    renameObject("${_networkId.id}")
   }
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -190,7 +190,7 @@ class Network constructor(
     // we don't set our ID!
     if (!info.networkName.isNullOrEmpty() && info.networkName != networkName())
       setNetworkName(info.networkName)
-    if (info.identity > 0 && info.identity != identity())
+    if (info.identity.isValidId() && info.identity != identity())
       setIdentity(info.identity)
     if (info.codecForServer != codecForServer())
       setCodecForServer(info.codecForServer)
@@ -858,7 +858,7 @@ class Network constructor(
       field = value
       live_networkInfo.onNext(Unit)
     }
-  private var _identity: IdentityId = -1
+  private var _identity: IdentityId = IdentityId(-1)
     set(value) {
       field = value
       live_networkInfo.onNext(Unit)
@@ -1063,6 +1063,6 @@ class Network constructor(
   }
 
   companion object {
-    val NULL = Network(-1, SignalProxy.NULL)
+    val NULL = Network(NetworkId(-1), SignalProxy.NULL)
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
index f7b83e55c..585ca94b5 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
@@ -27,8 +27,8 @@ import de.kuschku.libquassel.protocol.Type
 @Syncable(name = "BacklogManager")
 interface IBacklogManager : ISyncableObject {
   @Slot
-  fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
-                     additional: Int = 0) {
+  fun requestBacklog(bufferId: BufferId, first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
+                     limit: Int = -1, additional: Int = 0) {
     REQUEST(
       "requestBacklog", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
       ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int)
@@ -36,9 +36,9 @@ interface IBacklogManager : ISyncableObject {
   }
 
   @Slot
-  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1,
-                             limit: Int = -1, additional: Int = 0, type: Int = -1,
-                             flags: Int = -1) {
+  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = MsgId(-1),
+                             last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0,
+                             type: Int = -1, flags: Int = -1) {
     REQUEST(
       "requestBacklogFiltered", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
       ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int),
@@ -47,7 +47,7 @@ interface IBacklogManager : ISyncableObject {
   }
 
   @Slot
-  fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
+  fun requestBacklogAll(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1,
                         additional: Int = 0) {
     REQUEST(
       "requestBacklogAll", ARG(first, QType.MsgId), ARG(last, QType.MsgId),
@@ -56,8 +56,9 @@ interface IBacklogManager : ISyncableObject {
   }
 
   @Slot
-  fun requestBacklogAllFiltered(first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
-                                additional: Int = 0, type: Int = -1, flags: Int = -1) {
+  fun requestBacklogAllFiltered(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
+                                limit: Int = -1, additional: Int = 0, type: Int = -1,
+                                flags: Int = -1) {
     REQUEST(
       "requestBacklogAllFiltered", ARG(first, QType.MsgId), ARG(last, QType.MsgId),
       ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int), ARG(flags, Type.Int)
@@ -77,8 +78,8 @@ interface IBacklogManager : ISyncableObject {
                         messages: QVariantList)
 
   @Slot
-  fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int,
-                                type: Int, flags: Int, messages: QVariantList)
+  fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int,
+                                flags: Int, messages: QVariantList)
 
   @Slot
   override fun update(properties: QVariantMap) {
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 701989b6b..42e8e8af6 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
@@ -295,9 +295,9 @@ interface INetwork : ISyncableObject {
   }
 
   data class NetworkInfo(
-    var networkId: NetworkId = -1,
+    var networkId: NetworkId = NetworkId(-1),
     var networkName: String = "",
-    var identity: IdentityId = -1,
+    var identity: IdentityId = IdentityId(-1),
     // unused
     var useCustomEncodings: Boolean = false,
     var codecForServer: String = "UTF_8",
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
index 7ae0ce7a4..b6f9ea207 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
@@ -34,7 +34,7 @@ import java.nio.ByteBuffer
 interface IRpcHandler {
   val session: Session
 
-  @Slot("__objectRenamed__")
+  @Slot(value = "__objectRenamed__")
   fun objectRenamed(classname: ByteBuffer, newname: String?, oldname: String?)
 
   @Slot("2displayMsg(Message)")
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
index 74c703b36..e5f4e366d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt
@@ -28,7 +28,7 @@ interface BacklogStorage {
   fun storeMessages(session: ISession, vararg messages: Message)
   fun storeMessages(session: ISession, messages: Iterable<Message>)
 
-  fun clearMessages(bufferId: BufferId, idRange: IntRange)
+  fun clearMessages(bufferId: BufferId, idRange: LongRange)
 
   fun clearMessages(bufferId: BufferId)
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
index 6d9cbaae6..5fc7fee8a 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
@@ -215,7 +215,7 @@ class Session(
       bufferSyncer.initSetBufferInfos(f.bufferInfos)
 
       f.networkIds?.forEach {
-        val network = Network(it.value(-1), this)
+        val network = Network(it.value(NetworkId(-1)), this)
         networks[network.networkId()] = network
       }
       live_networks.onNext(Unit)
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt
index bf1928f4d..c7989949c 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt
@@ -19,7 +19,9 @@
 
 package de.kuschku.libquassel.protocol.primitive.serializer
 
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
@@ -30,8 +32,8 @@ class BufferInfoSerializerTest {
   @Test
   fun testBaseCase() {
     val value = BufferInfo(
-      -1,
-      -1,
+      BufferId(-1),
+      NetworkId(-1),
       Buffer_Type.of(),
       -1,
       ""
@@ -45,8 +47,8 @@ class BufferInfoSerializerTest {
   @Test
   fun testNormal() {
     val value = BufferInfo(
-      Int.MAX_VALUE,
-      Int.MAX_VALUE,
+      BufferId.MAX_VALUE,
+      NetworkId.MAX_VALUE,
       Buffer_Type.of(*Buffer_Type.validValues),
       Int.MAX_VALUE,
       "äẞ\u0000\uFFFF"
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt
index 19be62a40..e4fe91853 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt
@@ -32,13 +32,13 @@ class MessageSerializerTest {
   @Test
   fun testBaseCaseNoFeatures() {
     val value = Message(
-      -1,
+      MsgId(-1),
       Instant.EPOCH,
       Message_Type.of(),
       Message_Flag.of(),
       BufferInfo(
-        -1,
-        -1,
+        BufferId(-1),
+        NetworkId(-1),
         Buffer_Type.of(),
         -1,
         ""
@@ -59,7 +59,7 @@ class MessageSerializerTest {
   @Test
   fun testNormalNoFeatures() {
     val value = Message(
-      Int.MAX_VALUE.toLong(),
+      MsgId(Int.MAX_VALUE.toLong()),
       Instant.ofEpochMilli(1524601750000),
       Message_Type.of(*Message_Type.values()),
       Message_Flag.of(*Message_Flag.values()),
@@ -86,13 +86,13 @@ class MessageSerializerTest {
   @Test
   fun testBaseCaseAllFeatures() {
     val value = Message(
-      -1,
+      MsgId(-1),
       Instant.EPOCH,
       Message_Type.of(),
       Message_Flag.of(),
       BufferInfo(
-        -1,
-        -1,
+        BufferId(-1),
+        NetworkId(-1),
         Buffer_Type.of(),
         -1,
         ""
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
index f98c1fa7d..1be52a9e5 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
@@ -19,8 +19,10 @@
 
 package de.kuschku.libquassel.quassel.syncables
 
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.Buffer_Types
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
@@ -64,8 +66,8 @@ class AliasManagerTest {
       manager.setAliasList(manager.defaults() + aliases)
 
       val bufferInfo = BufferInfo(
-        bufferId = -1,
-        networkId = -1,
+        bufferId = BufferId(-1),
+        networkId = NetworkId(-1),
         type = Buffer_Types.of(Buffer_Type.StatusBuffer),
         bufferName = "#quassel-test",
         groupId = -1
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt
index fe468b734..b79f480d6 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt
@@ -21,6 +21,7 @@ package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.Buffer_Activity
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.*
@@ -31,7 +32,7 @@ class BufferViewConfigTest {
   fun testSerialization() {
     val original = BufferViewConfig(randomInt(), SignalProxy.NULL)
     original.setBufferViewName(randomString())
-    original.setNetworkId(randomInt())
+    original.setNetworkId(NetworkId(randomInt()))
     original.setAddNewBuffersAutomatically(randomBoolean())
     original.setSortAlphabetically(randomBoolean())
     original.setHideInactiveNetworks(randomBoolean())
@@ -54,7 +55,7 @@ class BufferViewConfigTest {
   fun testCopy() {
     val original = BufferViewConfig(randomInt(), SignalProxy.NULL)
     original.setBufferViewName(randomString())
-    original.setNetworkId(randomInt())
+    original.setNetworkId(NetworkId(randomInt()))
     original.setAddNewBuffersAutomatically(randomBoolean())
     original.setSortAlphabetically(randomBoolean())
     original.setHideInactiveNetworks(randomBoolean())
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt
index d23fdb3c7..7efacedbc 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.libquassel.quassel.syncables
 
+import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.randomInt
@@ -30,7 +31,7 @@ class IdentityTest {
   @Test
   fun testSerialization() {
     val original = Identity(SignalProxy.NULL)
-    original.setId(randomInt())
+    original.setId(IdentityId(randomInt()))
     original.setIdentityName(randomString())
     original.setRealName(randomString())
     original.setNicks(listOf(
@@ -50,7 +51,7 @@ class IdentityTest {
   @Test
   fun testCopy() {
     val original = Identity(SignalProxy.NULL)
-    original.setId(randomInt())
+    original.setId(IdentityId(randomInt()))
     original.setIdentityName(randomString())
     original.setRealName(randomString())
     original.setNicks(listOf(
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt
index ffb0309bf..4d0088107 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt
@@ -19,6 +19,8 @@
 
 package de.kuschku.libquassel.quassel.syncables
 
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.session.SignalProxy
@@ -28,9 +30,9 @@ import org.junit.Test
 class NetworkTest {
   @Test
   fun testSerialization() {
-    val original = Network(randomInt(), SignalProxy.NULL)
+    val original = Network(NetworkId(randomInt()), SignalProxy.NULL)
     original.setNetworkName(randomString())
-    original.setIdentity(randomInt())
+    original.setIdentity(IdentityId(randomInt()))
     original.setActualServerList(listOf(
       INetwork.Server(
         host = randomString(),
@@ -155,9 +157,9 @@ class NetworkTest {
 
   @Test
   fun testCopy() {
-    val original = Network(randomInt(), SignalProxy.NULL)
+    val original = Network(NetworkId(randomInt()), SignalProxy.NULL)
     original.setNetworkName(randomString())
-    original.setIdentity(randomInt())
+    original.setIdentity(IdentityId(randomInt()))
     original.setActualServerList(listOf(
       INetwork.Server(
         host = randomString(),
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt
index b3be2f176..83e7ce99c 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
+import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT
 import de.kuschku.libquassel.util.roundTrip
@@ -30,7 +31,7 @@ class INetworkInfoTest {
   fun testSerialization() {
     val original = INetwork.NetworkInfo(
       networkName = "QuakeNet",
-      identity = 5,
+      identity = IdentityId(5),
       serverList = listOf(
         INetwork.Server(
           host = "irc.quakenet.org",
@@ -47,7 +48,7 @@ class INetworkInfoTest {
   fun testCopy() {
     val original = INetwork.NetworkInfo(
       networkName = "QuakeNet",
-      identity = 5,
+      identity = IdentityId(5),
       serverList = listOf(
         INetwork.Server(
           host = "irc.quakenet.org",
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt b/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt
index c3319d935..5eee69af8 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt
@@ -23,7 +23,7 @@ import org.threeten.bp.Instant
 import java.nio.charset.Charset
 import java.util.*
 
-val random = Random()
+private val random = Random()
 
 fun Any?.randomBoolean(): Boolean = random.nextBoolean()
 
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
index 1416124c1..1e5702931 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
@@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.persistence
 
 import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.IgnoreListManager
 import de.kuschku.libquassel.session.BacklogStorage
 import de.kuschku.libquassel.session.ISession
@@ -39,7 +40,7 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
 
   override fun storeMessages(session: ISession, messages: Iterable<Message>) {
     db.message().save(*messages.map {
-      QuasselDatabase.MessageData(
+      QuasselDatabase.MessageData.of(
         messageId = it.messageId,
         time = it.time,
         type = it.type,
@@ -56,12 +57,12 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
     }.toTypedArray())
   }
 
-  override fun clearMessages(bufferId: BufferId, idRange: IntRange) {
-    db.message().clearMessages(bufferId, idRange.first, idRange.last)
+  override fun clearMessages(bufferId: BufferId, idRange: LongRange) {
+    db.message().clearMessages(bufferId.id, idRange.first, idRange.last)
   }
 
   override fun clearMessages(bufferId: BufferId) {
-    db.message().clearMessages(bufferId)
+    db.message().clearMessages(bufferId.id)
   }
 
   override fun clearMessages() {
@@ -82,7 +83,7 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
     fun isIgnored(session: ISession, message: QuasselDatabase.MessageData): Boolean {
       val bufferInfo = session.bufferSyncer?.bufferInfo(message.bufferId)
       val bufferName = bufferInfo?.bufferName ?: ""
-      val networkId = bufferInfo?.networkId ?: -1
+      val networkId = bufferInfo?.networkId ?: NetworkId(-1)
       val networkName = session.network(networkId)?.networkName() ?: ""
 
       return session.ignoreListManager?.match(
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
index 5ac9d4e44..8c56bff6c 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
@@ -20,7 +20,6 @@
 package de.kuschku.quasseldroid.persistence
 
 import android.content.Context
-import androidx.annotation.IntRange
 import androidx.lifecycle.LiveData
 import androidx.paging.DataSource
 import androidx.room.*
@@ -43,19 +42,60 @@ abstract class QuasselDatabase : RoomDatabase() {
 
   @Entity(tableName = "message", indices = [Index("bufferId"), Index("ignored")])
   data class MessageData(
-    @PrimaryKey var messageId: MsgId,
+    @PrimaryKey
+    @ColumnInfo(name = "messageId")
+    var rawMessageId: MsgId_Type,
     var time: Instant,
     var type: Message_Types,
     var flag: Message_Flags,
-    var bufferId: BufferId,
-    var networkId: NetworkId,
+    @ColumnInfo(name = "bufferId")
+    var rawBufferId: BufferId_Type,
+    @ColumnInfo(name = "networkId")
+    var rawNetworkId: NetworkId_Type,
     var sender: String,
     var senderPrefixes: String,
     var realName: String,
     var avatarUrl: String,
     var content: String,
     var ignored: Boolean
-  )
+  ) {
+    inline val messageId
+      get() = MsgId(rawMessageId)
+    inline val bufferId
+      get() = BufferId(rawBufferId)
+    inline val networkId
+      get() = NetworkId(rawNetworkId)
+
+    companion object {
+      inline fun of(
+        messageId: MsgId,
+        time: Instant,
+        type: Message_Types,
+        flag: Message_Flags,
+        bufferId: BufferId,
+        networkId: NetworkId,
+        sender: String,
+        senderPrefixes: String,
+        realName: String,
+        avatarUrl: String,
+        content: String,
+        ignored: Boolean
+      ) = MessageData(
+        messageId.id,
+        time,
+        type,
+        flag,
+        bufferId.id,
+        networkId.id,
+        sender,
+        senderPrefixes,
+        realName,
+        avatarUrl,
+        content,
+        ignored
+      )
+    }
+  }
 
   @Dao
   interface MessageDao {
@@ -63,79 +103,96 @@ abstract class QuasselDatabase : RoomDatabase() {
     fun all(): List<MessageData>
 
     @Query("SELECT DISTINCT bufferId FROM message")
-    fun buffers(): List<BufferId>
+    fun _buffers(): List<BufferId_Type>
 
     @Query("SELECT * FROM message WHERE messageId = :messageId")
-    fun find(messageId: Int): MessageData?
+    fun find(messageId: MsgId_Type): MessageData?
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC")
-    fun findByBufferId(bufferId: Int): List<MessageData>
+    fun _findByBufferId(bufferId: BufferId_Type): List<MessageData>
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId DESC")
-    fun findByBufferIdPaged(bufferId: Int, type: Int): DataSource.Factory<Int, MessageData>
+    fun _findByBufferIdPaged(bufferId: BufferId_Type,
+                             type: Int): DataSource.Factory<Int, MessageData>
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1")
-    fun findLastByBufferId(bufferId: Int): MessageData?
+    fun _findLastByBufferId(bufferId: BufferId_Type): MessageData?
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1")
-    fun lastMsgId(bufferId: Int): LiveData<MessageData>
+    fun _lastMsgId(bufferId: BufferId_Type): LiveData<MessageData>
 
     @Query("SELECT messageId FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1")
-    fun firstMsgId(bufferId: Int): Flowable<MsgId>
+    fun _firstMsgId(bufferId: BufferId_Type): Flowable<MsgId_Type>
 
     @Query("SELECT messageId FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId ASC LIMIT 1")
-    fun firstVisibleMsgId(bufferId: Int, type: Int): MsgId?
+    fun _firstVisibleMsgId(bufferId: BufferId_Type, type: Int): MsgId_Type?
 
     @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1")
-    fun findFirstByBufferId(bufferId: Int): MessageData?
+    fun _findFirstByBufferId(bufferId: BufferId_Type): MessageData?
 
     @Query("SELECT EXISTS(SELECT 1 FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0)")
-    fun hasVisibleMessages(bufferId: Int, type: Int): Boolean
+    fun _hasVisibleMessages(bufferId: BufferId_Type, type: Int): Boolean
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun save(vararg entities: MessageData)
 
     @Query("UPDATE message SET bufferId = :bufferId1 WHERE bufferId = :bufferId2")
-    fun merge(@IntRange(from = 0) bufferId1: Int, @IntRange(from = 0) bufferId2: Int)
+    fun _merge(bufferId1: BufferId_Type, bufferId2: BufferId_Type)
 
     @Query("SELECT count(*) FROM message WHERE bufferId = :bufferId")
-    fun bufferSize(@IntRange(from = 0) bufferId: Int): Int
+    fun _bufferSize(bufferId: BufferId_Type): Int
 
     @Query("DELETE FROM message")
     fun clearMessages()
 
     @Query("DELETE FROM message WHERE bufferId = :bufferId")
-    fun clearMessages(@IntRange(from = 0) bufferId: Int)
+    fun clearMessages(bufferId: BufferId_Type)
 
     @Query(
       "DELETE FROM message WHERE bufferId = :bufferId AND messageId >= :first AND messageId <= :last"
     )
-    fun clearMessages(@IntRange(from = 0) bufferId: Int, first: Int, last: Int)
+    fun clearMessages(bufferId: BufferId_Type, first: MsgId_Type, last: MsgId_Type)
   }
 
   @Entity(tableName = "filtered", primaryKeys = ["accountId", "bufferId"])
   data class Filtered(
     var accountId: Long,
-    var bufferId: BufferId,
+    @ColumnInfo(name = "bufferId")
+    var rawBufferId: BufferId_Type,
     var filtered: Int
-  )
+  ) {
+    inline val bufferId
+      get() = BufferId(rawBufferId)
+
+    companion object {
+      inline fun of(
+        accountId: Long,
+        bufferId: BufferId,
+        filtered: Int
+      ) = Filtered(
+        accountId,
+        bufferId.id,
+        filtered
+      )
+    }
+  }
 
   @Dao
   interface FilteredDao {
     @Query("SELECT DISTINCT bufferId FROM filtered WHERE accountId = :accountId")
-    fun buffers(accountId: Long): List<BufferId>
+    fun _buffers(accountId: Long): List<BufferId_Type>
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun replace(vararg entities: Filtered)
 
     @Query("UPDATE filtered SET filtered = :filtered WHERE accountId = :accountId AND bufferId = :bufferId")
-    fun setFiltered(accountId: Long, bufferId: Int, filtered: Int)
+    fun _setFiltered(accountId: Long, bufferId: BufferId_Type, filtered: Int)
 
     @Query("SELECT IFNULL(t.filtered, :defaultValue) FROM (SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT NULL ORDER BY filtered DESC LIMIT 1) t")
-    fun get(accountId: Long, bufferId: Int, defaultValue: Int): Int
+    fun _get(accountId: Long, bufferId: BufferId_Type, defaultValue: Int): Int
 
     @Query("SELECT IFNULL(t.filtered, :defaultValue) FROM (SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT NULL ORDER BY filtered DESC LIMIT 1) t")
-    fun listen(accountId: Long, bufferId: Int, defaultValue: Int): LiveData<Int>
+    fun _listen(accountId: Long, bufferId: BufferId_Type, defaultValue: Int): LiveData<Int>
 
     @Query("SELECT * FROM filtered WHERE accountId = :accountId")
     fun listen(accountId: Long): LiveData<List<Filtered>>
@@ -147,7 +204,7 @@ abstract class QuasselDatabase : RoomDatabase() {
     fun clear(accountId: Long)
 
     @Query("DELETE FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId")
-    fun clear(accountId: Long, bufferId: Int)
+    fun _clear(accountId: Long, bufferId: BufferId_Type)
   }
 
   @Entity(tableName = "ssl_validity_whitelist")
@@ -201,15 +258,19 @@ abstract class QuasselDatabase : RoomDatabase() {
 
   @Entity(tableName = "notification", indices = [Index("bufferId")])
   data class NotificationData(
-    @PrimaryKey var messageId: MsgId,
+    @PrimaryKey
+    @ColumnInfo(name = "messageId")
+    var rawMessageId: MsgId_Type,
     var creationTime: Instant,
     var time: Instant,
     var type: Message_Types,
     var flag: Message_Flags,
-    var bufferId: BufferId,
+    @ColumnInfo(name = "bufferId")
+    var rawBufferId: BufferId_Type,
     var bufferName: String,
     var bufferType: Buffer_Types,
-    var networkId: NetworkId,
+    @ColumnInfo(name = "networkId")
+    var rawNetworkId: NetworkId_Type,
     var networkName: String,
     var sender: String,
     var senderPrefixes: String,
@@ -221,7 +282,62 @@ abstract class QuasselDatabase : RoomDatabase() {
     var ownRealName: String,
     var ownAvatarUrl: String,
     var hidden: Boolean
-  )
+  ) {
+    inline val messageId
+      get() = MsgId(rawMessageId)
+
+    inline val bufferId
+      get() = BufferId(rawBufferId)
+
+    inline val networkId
+      get() = NetworkId(rawNetworkId)
+
+    companion object {
+      inline fun of(
+        messageId: MsgId,
+        creationTime: Instant,
+        time: Instant,
+        type: Message_Types,
+        flag: Message_Flags,
+        bufferId: BufferId,
+        bufferName: String,
+        bufferType: Buffer_Types,
+        networkId: NetworkId,
+        networkName: String,
+        sender: String,
+        senderPrefixes: String,
+        realName: String,
+        avatarUrl: String,
+        content: String,
+        ownNick: String,
+        ownIdent: String,
+        ownRealName: String,
+        ownAvatarUrl: String,
+        hidden: Boolean
+      ) = NotificationData(
+        messageId.id,
+        creationTime,
+        time,
+        type,
+        flag,
+        bufferId.id,
+        bufferName,
+        bufferType,
+        networkId.id,
+        networkName,
+        sender,
+        senderPrefixes,
+        realName,
+        avatarUrl,
+        content,
+        ownNick,
+        ownIdent,
+        ownRealName,
+        ownAvatarUrl,
+        hidden
+      )
+    }
+  }
 
   @Dao
   interface NotificationDao {
@@ -229,25 +345,25 @@ abstract class QuasselDatabase : RoomDatabase() {
     fun save(vararg entities: NotificationData)
 
     @Query("SELECT DISTINCT bufferId FROM notification")
-    fun buffers(): List<BufferId>
+    fun _buffers(): List<BufferId_Type>
 
     @Query("SELECT * FROM notification WHERE hidden = 0 ORDER BY time ASC")
     fun all(): List<NotificationData>
 
     @Query("SELECT * FROM notification WHERE bufferId = :bufferId AND hidden = 0 ORDER BY time ASC")
-    fun all(bufferId: BufferId): List<NotificationData>
+    fun _all(bufferId: BufferId_Type): List<NotificationData>
 
     @Query("UPDATE notification SET hidden = 1 WHERE bufferId = :bufferId AND messageId <= :messageId")
-    fun markHidden(bufferId: BufferId, messageId: MsgId)
+    fun _markHidden(bufferId: BufferId_Type, messageId: MsgId_Type)
 
     @Query("UPDATE notification SET hidden = 1 WHERE bufferId = :bufferId AND flag & 2 = 0")
-    fun markHiddenNormal(bufferId: BufferId)
+    fun _markHiddenNormal(bufferId: BufferId_Type)
 
     @Query("DELETE FROM notification WHERE bufferId = :bufferId AND messageId <= :messageId")
-    fun markRead(bufferId: BufferId, messageId: MsgId)
+    fun _markRead(bufferId: BufferId_Type, messageId: MsgId_Type)
 
     @Query("DELETE FROM notification WHERE bufferId = :bufferId AND flag & 2 = 0")
-    fun markReadNormal(bufferId: BufferId)
+    fun _markReadNormal(bufferId: BufferId_Type)
 
     @Query("DELETE FROM notification")
     fun clear()
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt
new file mode 100644
index 000000000..2e3434c0f
--- /dev/null
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt
@@ -0,0 +1,91 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.persistence
+
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.MsgId
+
+inline fun QuasselDatabase.MessageDao.buffers() =
+  _buffers().map { BufferId(it) }
+
+inline fun QuasselDatabase.MessageDao.findByBufferId(bufferId: BufferId) =
+  _findByBufferId(bufferId.id)
+
+inline fun QuasselDatabase.MessageDao.findByBufferIdPaged(bufferId: BufferId, type: Int) =
+  _findByBufferIdPaged(bufferId.id, type)
+
+inline fun QuasselDatabase.MessageDao.findLastByBufferId(bufferId: BufferId) =
+  _findLastByBufferId(bufferId.id)
+
+inline fun QuasselDatabase.MessageDao.lastMsgId(bufferId: BufferId) =
+  _lastMsgId(bufferId.id)
+
+inline fun QuasselDatabase.MessageDao.firstMsgId(bufferId: BufferId) =
+  _firstMsgId(bufferId.id).map(::MsgId)
+
+inline fun QuasselDatabase.MessageDao.firstVisibleMsgId(bufferId: BufferId, type: Int) =
+  _firstVisibleMsgId(bufferId.id, type)?.let(::MsgId)
+
+inline fun QuasselDatabase.MessageDao.findFirstByBufferId(bufferId: BufferId) =
+  _findFirstByBufferId(bufferId.id)
+
+inline fun QuasselDatabase.MessageDao.hasVisibleMessages(bufferId: BufferId, type: Int) =
+  _hasVisibleMessages(bufferId.id, type)
+
+inline fun QuasselDatabase.MessageDao.merge(bufferId1: BufferId, bufferId2: BufferId) =
+  _merge(bufferId1.id, bufferId2.id)
+
+inline fun QuasselDatabase.MessageDao.bufferSize(bufferId: BufferId) =
+  _bufferSize(bufferId.id)
+
+inline fun QuasselDatabase.NotificationDao.buffers() =
+  _buffers().map(::BufferId)
+
+inline fun QuasselDatabase.NotificationDao.all(bufferId: BufferId) =
+  _all(bufferId.id)
+
+inline fun QuasselDatabase.NotificationDao.markHidden(bufferId: BufferId, messageId: MsgId) =
+  _markHidden(bufferId.id, messageId.id)
+
+inline fun QuasselDatabase.NotificationDao.markHiddenNormal(bufferId: BufferId) =
+  _markHiddenNormal(bufferId.id)
+
+inline fun QuasselDatabase.NotificationDao.markRead(bufferId: BufferId, messageId: MsgId) =
+  _markRead(bufferId.id, messageId.id)
+
+inline fun QuasselDatabase.NotificationDao.markReadNormal(bufferId: BufferId) =
+  _markReadNormal(bufferId.id)
+
+inline fun QuasselDatabase.FilteredDao.buffers(accountId: Long) =
+  _buffers(accountId).map(::BufferId)
+
+inline fun QuasselDatabase.FilteredDao.setFiltered(accountId: Long, bufferId: BufferId,
+                                                   filtered: Int) =
+  _setFiltered(accountId, bufferId.id, filtered)
+
+inline fun QuasselDatabase.FilteredDao.get(accountId: Long, bufferId: BufferId, defaultValue: Int) =
+  _get(accountId, bufferId.id, defaultValue)
+
+inline fun QuasselDatabase.FilteredDao.listen(accountId: Long, bufferId: BufferId,
+                                              defaultValue: Int) =
+  _listen(accountId, bufferId.id, defaultValue)
+
+inline fun QuasselDatabase.FilteredDao.clear(accountId: Long, bufferId: BufferId) =
+  _clear(accountId, bufferId.id)
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
index bb2e110d5..52df1deb6 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
@@ -20,6 +20,7 @@
 package de.kuschku.quasseldroid.viewmodel
 
 import androidx.lifecycle.ViewModel
+import de.kuschku.libquassel.protocol.BufferId
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.quassel.syncables.AliasManager
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
@@ -45,7 +46,7 @@ class EditorViewModel : ViewModel() {
 
   val lastWord = BehaviorSubject.create<Observable<Pair<String, IntRange>>>()
 
-  val rawAutoCompleteData: Observable<Triple<Optional<ISession>, Int, Pair<String, IntRange>>> =
+  val rawAutoCompleteData: Observable<Triple<Optional<ISession>, BufferId, Pair<String, IntRange>>> =
     combineLatest(session, buffer, lastWord).switchMap { (sessionOptional, id, lastWordWrapper) ->
       lastWordWrapper
         .distinctUntilChanged()
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
index 619f58cc8..d185a84da 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
@@ -68,7 +68,7 @@ class QuasselViewModel : ViewModel() {
 
   val expandedMessages = BehaviorSubject.createDefault(emptySet<MsgId>())
 
-  val buffer = BehaviorSubject.createDefault(Int.MAX_VALUE)
+  val buffer = BehaviorSubject.createDefault(BufferId.MAX_VALUE)
   val bufferOpened = PublishSubject.create<Unit>()
 
   val bufferViewConfigId = BehaviorSubject.createDefault(-1)
@@ -301,7 +301,7 @@ class QuasselViewModel : ViewModel() {
 
   val showHidden = BehaviorSubject.createDefault(false)
   val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>())
-  val selectedBufferId = BehaviorSubject.createDefault(Int.MAX_VALUE)
+  val selectedBufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE)
   val selectedBuffer = combineLatest(session, selectedBufferId, bufferViewConfig)
     .switchMap { (sessionOptional, buffer, bufferViewConfigOptional) ->
       val session = sessionOptional.orNull()
@@ -318,7 +318,7 @@ class QuasselViewModel : ViewModel() {
               BufferHiddenState.VISIBLE
           }
 
-          val info = if (buffer < 0) networks[-buffer]?.let {
+          val info = if (!buffer.isValidId()) networks[NetworkId(-buffer.id)]?.let {
             BufferInfo(
               bufferId = buffer,
               networkId = it.networkId(),
@@ -386,10 +386,10 @@ class QuasselViewModel : ViewModel() {
                       it.type.hasFlag(Buffer_Type.StatusBuffer) ||
                       it.bufferName?.contains(bufferSearch, ignoreCase = true) == true
                     }.filter {
-                      currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
+                      !currentConfig.networkId().isValidId() || currentConfig.networkId() == it.networkId
                     }.filter {
                       (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() ||
-                      (it.type.hasFlag(Buffer_Type.StatusBuffer) && currentConfig.networkId() < 0)
+                      (it.type.hasFlag(Buffer_Type.StatusBuffer) && !currentConfig.networkId().isValidId())
                     }.mapNotNull {
                       val network = networks[it.networkId]
                       if (network == null) {
@@ -489,7 +489,7 @@ class QuasselViewModel : ViewModel() {
                   fun missingStatusBuffers(
                     list: Collection<BufferId>): Sequence<Observable<BufferProps>?> {
                     val totalNetworks = networks.keys
-                    val wantedNetworks = if (currentConfig.networkId() <= 0) totalNetworks
+                    val wantedNetworks = if (!currentConfig.networkId().isValidId()) totalNetworks
                     else listOf(currentConfig.networkId())
 
                     val availableNetworks = list.asSequence().mapNotNull { id ->
@@ -503,7 +503,7 @@ class QuasselViewModel : ViewModel() {
                     val missingNetworks = wantedNetworks - availableNetworks
 
                     return missingNetworks.asSequence().filter {
-                      currentConfig.networkId() <= 0 || currentConfig.networkId() == it
+                      !currentConfig.networkId().isValidId() || currentConfig.networkId() == it
                     }.filter {
                       currentConfig.allowedBufferTypes().hasFlag(Buffer_Type.StatusBuffer)
                     }.mapNotNull {
@@ -515,7 +515,7 @@ class QuasselViewModel : ViewModel() {
                         network.liveConnectionState().map { connectionState ->
                           BufferProps(
                             info = BufferInfo(
-                              bufferId = -networkInfo.networkId,
+                              bufferId = BufferId(-networkInfo.networkId.id),
                               networkId = networkInfo.networkId,
                               groupId = 0,
                               bufferName = networkInfo.networkName,
-- 
GitLab