diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 9b254dd23125955075ab30a1fe35970dc184dc1f..f05695102ee42341918abf40b973d4ba06395611 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -124,14 +124,16 @@ dependencies {
   implementation(libs.dagger.android.core)
   implementation(libs.dagger.android.support)
 
-  testImplementation(libs.junit)
+  testImplementation(libs.junit.api)
+  testRuntimeOnly(libs.junit.engine)
   testImplementation(libs.androidx.test.core)
   testImplementation(libs.robolectric) {
     exclude(group = "org.threeten", module = "threetenbp")
     exclude(group = "com.google.auto.service", module = "auto-service")
   }
 
-  androidTestImplementation(libs.junit)
+  androidTestImplementation(libs.junit.api)
+  androidTestRuntimeOnly(libs.junit.engine)
   androidTestImplementation(libs.androidx.test.espresso.core)
   androidTestImplementation(libs.androidx.test.espresso.contrib)
   androidTestImplementation(libs.androidx.test.junit)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ab32d2076c907faeb1976e2283e78f2e6af15b64..0184736b797d025288c51ee394e5be83d6affc2f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,6 +30,7 @@
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
   <application
     android:name="de.kuschku.quasseldroid.Quasseldroid"
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 e4f65134789a781e636d7b51e03e77d200b78db1..e26ce8a87f37ecd38621124878006f84ea1bc123 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.quasseldroid.service
 
+import android.Manifest
 import android.annotation.TargetApi
 import android.app.Notification
 import android.app.NotificationChannel
@@ -26,11 +27,13 @@ import android.app.NotificationManager
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.drawable.Drawable
 import android.net.Uri
 import android.os.Build
+import androidx.core.app.ActivityCompat
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.app.Person
@@ -63,7 +66,8 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
 
   @TargetApi(Build.VERSION_CODES.O)
   private fun prepareChannels() {
-    val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+    val notificationManager =
+      context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
     notificationManager.createNotificationChannels(
       listOf(
         NotificationChannel(
@@ -109,9 +113,11 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
     return bitmap
   }
 
-  fun notificationMessage(notificationSettings: NotificationSettings, buffer: NotificationBuffer,
-                          selfInfo: SelfInfo, notifications: List<NotificationMessage>,
-                          isLoud: Boolean, isConnected: Boolean): Handle {
+  fun notificationMessage(
+    notificationSettings: NotificationSettings, buffer: NotificationBuffer,
+    selfInfo: SelfInfo, notifications: List<NotificationMessage>,
+    isLoud: Boolean, isConnected: Boolean
+  ): Handle {
     val pendingIntentOpen = PendingIntent.getActivity(
       context.applicationContext,
       System.currentTimeMillis().toInt(),
@@ -279,7 +285,13 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C
   }
 
   fun notify(handle: Handle) {
-    notificationManagerCompat.notify(handle.id, handle.builder.build())
+    if (ActivityCompat.checkSelfPermission(
+        context.applicationContext,
+        Manifest.permission.POST_NOTIFICATIONS
+      ) == PackageManager.PERMISSION_GRANTED
+    ) {
+      notificationManagerCompat.notify(handle.id, handle.builder.build())
+    }
   }
 
   fun remove(handle: 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 32734dcb69e03c20cbd3798d59d5a48e68de5e26..9e971b1d99603fd9209fe8227b6052e0a0328a15 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
@@ -19,11 +19,13 @@
 
 package de.kuschku.quasseldroid.ui.chat
 
+import android.Manifest
 import android.annotation.SuppressLint
 import android.app.Activity
 import android.content.Context
 import android.content.Intent
 import android.content.SharedPreferences
+import android.content.pm.PackageManager
 import android.os.Build
 import android.os.Bundle
 import android.os.PersistableBundle
@@ -35,7 +37,9 @@ import android.view.MenuItem
 import android.view.View
 import android.widget.EditText
 import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
 import androidx.appcompat.app.ActionBarDrawerToggle
+import androidx.core.app.ActivityCompat
 import androidx.core.view.GravityCompat
 import androidx.drawerlayout.widget.DrawerLayout
 import androidx.lifecycle.Observer
@@ -71,6 +75,7 @@ import de.kuschku.quasseldroid.persistence.models.Filtered
 import de.kuschku.quasseldroid.persistence.models.SslHostnameWhitelistEntry
 import de.kuschku.quasseldroid.persistence.models.SslValidityWhitelistEntry
 import de.kuschku.quasseldroid.persistence.util.AccountId
+import de.kuschku.quasseldroid.service.QuasselNotificationBackend
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.settings.NotificationSettings
@@ -142,6 +147,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   @Inject
   lateinit var autoCompleteAdapter: AutoCompleteAdapter
 
+  @Inject
+  lateinit var notificationBackend: QuasselNotificationBackend
+
   lateinit var editorBottomSheet: DragInterceptBottomSheetBehavior<View>
 
   private val dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
@@ -155,43 +163,47 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   private var restoredDrawerState = false
 
   fun processIntent(intent: Intent) {
-      when {
-        intent.type == "text/plain"    -> {
-          val text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)
-          if (text != null) {
-            chatlineFragment?.replaceText(text)
-            binding.drawerLayout.closeDrawers()
-          }
+    when {
+      intent.type == "text/plain" -> {
+        val text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)
+        if (text != null) {
+          chatlineFragment?.replaceText(text)
+          binding.drawerLayout.closeDrawers()
         }
-        intent.hasExtra(KEY_BUFFER_ID) -> {
-          chatViewModel.bufferId.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1)))
-          chatViewModel.bufferOpened.onNext(Unit)
-          if (intent.hasExtra(KEY_ACCOUNT_ID)) {
-            val accountId = AccountId(intent.getLongExtra(KEY_ACCOUNT_ID, -1L))
-            if (accountId != this.accountId) {
-              resetAccount()
-              connectToAccount(accountId)
-              startedSelection = false
-              connectedAccount = AccountId(-1L)
-              checkConnection()
-              recreate()
-            }
+      }
+
+      intent.hasExtra(KEY_BUFFER_ID) -> {
+        chatViewModel.bufferId.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1)))
+        chatViewModel.bufferOpened.onNext(Unit)
+        if (intent.hasExtra(KEY_ACCOUNT_ID)) {
+          val accountId = AccountId(intent.getLongExtra(KEY_ACCOUNT_ID, -1L))
+          if (accountId != this.accountId) {
+            resetAccount()
+            connectToAccount(accountId)
+            startedSelection = false
+            connectedAccount = AccountId(-1L)
+            checkConnection()
+            recreate()
           }
         }
-        intent.hasExtra(KEY_AUTOCOMPLETE_TEXT)                            -> {
-          chatlineFragment?.editorHelper?.appendText(
-            intent.getStringExtra(KEY_AUTOCOMPLETE_TEXT),
-            intent.getStringExtra(KEY_AUTOCOMPLETE_SUFFIX)
-          )
-          binding.drawerLayout.closeDrawers()
-        }
-        intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL)   -> {
-          val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
-          val channel = intent.getStringExtra(KEY_CHANNEL) ?: ""
+      }
+
+      intent.hasExtra(KEY_AUTOCOMPLETE_TEXT) -> {
+        chatlineFragment?.editorHelper?.appendText(
+          intent.getStringExtra(KEY_AUTOCOMPLETE_TEXT),
+          intent.getStringExtra(KEY_AUTOCOMPLETE_SUFFIX)
+        )
+        binding.drawerLayout.closeDrawers()
+      }
+
+      intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL) -> {
+        val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
+        val channel = intent.getStringExtra(KEY_CHANNEL) ?: ""
 
-          val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
+        val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
 
-          modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement().subscribe {
+        modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement()
+          .subscribe {
             it.orNull()?.also { session ->
               val info = session.bufferSyncer.find(
                 bufferName = channel,
@@ -202,9 +214,11 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
               if (info != null && !forceJoin) {
                 ChatActivity.launch(this, bufferId = info.bufferId)
               } else {
-                modelHelper.chat.chatToJoin.onNext(Optional.of(
-                  Pair(networkId, channel)
-                ))
+                modelHelper.chat.chatToJoin.onNext(
+                  Optional.of(
+                    Pair(networkId, channel)
+                  )
+                )
 
                 session.bufferSyncer.find(
                   networkId = networkId,
@@ -217,14 +231,16 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
               }
             }
           }
-        }
-        intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_NICK_NAME) -> {
-          val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
-          val channel = intent.getStringExtra(KEY_NICK_NAME)
+      }
 
-          val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
+      intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_NICK_NAME) -> {
+        val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
+        val channel = intent.getStringExtra(KEY_NICK_NAME)
 
-          modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement().subscribe {
+        val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
+
+        modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement()
+          .subscribe {
             it.orNull()?.also { session ->
               val info = session.bufferSyncer.find(
                 bufferName = channel,
@@ -238,8 +254,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                 modelHelper.allBuffers.map {
                   listOfNotNull(it.find {
                     it.networkId == networkId &&
-                    it.bufferName == channel &&
-                    it.type.hasFlag(Buffer_Type.QueryBuffer)
+                      it.bufferName == channel &&
+                      it.type.hasFlag(Buffer_Type.QueryBuffer)
                   })
                 }.filter {
                   it.isNotEmpty()
@@ -260,31 +276,32 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
               }
             }
           }
-        }
-        intent.scheme == "irc" ||
-        intent.scheme == "ircs"                                           -> {
-          val uri = intent.data
-          if (uri != null) {
-            val channelString = (uri.path.let { it ?: "" }.trimStart('/')) +
-                                (uri.fragment?.let { "#$it" }.let { it ?: "" })
-            NetworkSetupActivity.launch(
-              this,
-              network = LinkNetwork(
-                name = "",
-                server = DefaultNetworkServer(
-                  host = uri.host ?: "",
-                  port = uri.port.nullIf { it < 0 }?.toUInt()
-                         ?: if (uri.scheme == "irc") PORT_PLAINTEXT.port
-                         else PORT_SSL.port,
-                  secure = uri.scheme == "ircs"
-                )
-              ),
-              channels = channelString.split(",").toTypedArray()
-            )
-          }
+      }
+
+      intent.scheme == "irc" ||
+        intent.scheme == "ircs" -> {
+        val uri = intent.data
+        if (uri != null) {
+          val channelString = (uri.path.let { it ?: "" }.trimStart('/')) +
+            (uri.fragment?.let { "#$it" }.let { it ?: "" })
+          NetworkSetupActivity.launch(
+            this,
+            network = LinkNetwork(
+              name = "",
+              server = DefaultNetworkServer(
+                host = uri.host ?: "",
+                port = uri.port.nullIf { it < 0 }?.toUInt()
+                  ?: if (uri.scheme == "irc") PORT_PLAINTEXT.port
+                  else PORT_SSL.port,
+                secure = uri.scheme == "ircs"
+              )
+            ),
+            channels = channelString.split(",").toTypedArray()
+          )
         }
       }
-      setIntent(null)
+    }
+    setIntent(null)
   }
 
   override fun onCreate(savedInstanceState: Bundle?) {
@@ -292,7 +309,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     binding = ActivityMainBinding.inflate(layoutInflater)
     setContentView(binding.root)
 
-    chatlineFragment = supportFragmentManager.findFragmentById(R.id.fragment_chatline) as? ChatlineFragment
+    chatlineFragment =
+      supportFragmentManager.findFragmentById(R.id.fragment_chatline) as? ChatlineFragment
 
     setSupportActionBar(binding.layoutMain.layoutToolbar.toolbar)
 
@@ -338,10 +356,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       accountDatabase.accounts().listenDefaultFiltered(accountId, 0).toObservable()
     )
 
-    val maxBufferActivity = modelHelper.processBufferList(modelHelper.bufferViewConfig,
-                                                          filtered).map { (config, bufferList) ->
+    val maxBufferActivity = modelHelper.processBufferList(
+      modelHelper.bufferViewConfig,
+      filtered
+    ).map { (config, bufferList) ->
       val minimumActivity: Buffer_Activity = config?.minimumActivity()?.enabledValues()?.maxOrNull()
-                                             ?: Buffer_Activity.NoActivity
+        ?: Buffer_Activity.NoActivity
 
       val maxActivity: Buffer_Activity = bufferList.mapNotNull {
         it.bufferActivity.enabledValues().maxOrNull()
@@ -349,11 +369,13 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
       val hasNotifications = bufferList.any { props ->
         when {
-          props.info.type hasFlag Buffer_Type.QueryBuffer   ->
+          props.info.type hasFlag Buffer_Type.QueryBuffer ->
             props.bufferActivity hasFlag Buffer_Activity.NewMessage
+
           props.info.type hasFlag Buffer_Type.ChannelBuffer ->
             props.highlights > 0
-          else                                              -> false
+
+          else -> false
         }
       }
 
@@ -366,28 +388,36 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
     supportActionBar?.apply {
       val toggleDefault = DrawerToggleActivityDrawable(themedContext, 0)
-      val toggleOtherActivity = DrawerToggleActivityDrawable(themedContext,
-                                                             R.attr.colorTintActivity)
+      val toggleOtherActivity = DrawerToggleActivityDrawable(
+        themedContext,
+        R.attr.colorTintActivity
+      )
       val toggleNewMessage = DrawerToggleActivityDrawable(themedContext, R.attr.colorTintMessage)
       val toggleHighlight = DrawerToggleActivityDrawable(themedContext, R.attr.colorTintHighlight)
-      val toggleNotification = DrawerToggleActivityDrawable(themedContext,
-                                                            R.attr.colorTintNotification)
+      val toggleNotification = DrawerToggleActivityDrawable(
+        themedContext,
+        R.attr.colorTintNotification
+      )
       maxBufferActivity.toLiveData()
         .observe(this@ChatActivity, Observer { (activity, hasNotifications) ->
           setHomeAsUpIndicator(
             when {
               notificationSettings.showAllActivitiesInToolbar &&
-              activity == Buffer_Activity.Highlight     ->
+                activity == Buffer_Activity.Highlight ->
                 toggleHighlight
+
               notificationSettings.showAllActivitiesInToolbar &&
-              activity == Buffer_Activity.NewMessage    ->
+                activity == Buffer_Activity.NewMessage ->
                 toggleNewMessage
+
               notificationSettings.showAllActivitiesInToolbar &&
-              activity == Buffer_Activity.OtherActivity ->
+                activity == Buffer_Activity.OtherActivity ->
                 toggleOtherActivity
-              hasNotifications                          ->
+
+              hasNotifications ->
                 toggleNotification
-              else                                      ->
+
+              else ->
                 toggleDefault
             }
           )
@@ -426,16 +456,17 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     modelHelper.errors.toLiveData(BackpressureStrategy.BUFFER).observe(this, Observer { error ->
       error?.let {
         when (it) {
-          is Error.HandshakeError  -> it.message.let {
+          is Error.HandshakeError -> it.message.let {
             when (it) {
-              is HandshakeMessage.ClientInitAck     ->
+              is HandshakeMessage.ClientInitAck ->
                 if (it.coreConfigured == false)
                   CoreSetupActivity.launch(
                     this,
                     accountDatabase.accounts().findById(accountId),
                     CoreSetupData.of(it)
                   )
-              is HandshakeMessage.ClientInitReject  ->
+
+              is HandshakeMessage.ClientInitReject ->
                 MaterialDialog.Builder(this)
                   .title(R.string.label_error_init)
                   .content(Html.fromHtml(it.errorString))
@@ -448,7 +479,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   .contentColorAttr(R.attr.colorTextPrimary)
                   .build()
                   .show()
-              is HandshakeMessage.CoreSetupReject   ->
+
+              is HandshakeMessage.CoreSetupReject ->
                 MaterialDialog.Builder(this)
                   .title(R.string.label_error_setup)
                   .content(Html.fromHtml(it.errorString))
@@ -461,6 +493,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   .contentColorAttr(R.attr.colorTextPrimary)
                   .build()
                   .show()
+
               is HandshakeMessage.ClientLoginReject ->
                 MaterialDialog.Builder(this)
                   .title(R.string.label_error_login)
@@ -514,10 +547,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   .contentColorAttr(R.attr.colorTextPrimary)
                   .build()
                   .show()
+
               else -> Unit // Do Nothing
             }
           }
-          is Error.SslError        -> {
+
+          is Error.SslError -> {
             it.exception.let {
               if (it == QuasselSecurityException.NoSsl) {
                 // Ssl is required but not available
@@ -547,8 +582,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   when {
                     // Certificate has expired
                     it is QuasselSecurityException.Certificate &&
-                    (it.cause is CertificateNotYetValidException ||
-                     it.cause is CertificateExpiredException)  -> {
+                      (it.cause is CertificateNotYetValidException ||
+                        it.cause is CertificateExpiredException) -> {
                       MaterialDialog.Builder(this)
                         .title(R.string.label_error_certificate)
                         .content(
@@ -556,10 +591,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                             getString(
                               R.string.label_error_certificate_invalid,
                               leafCertificate.sha1Fingerprint,
-                              dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notBefore.time)
-                                                         .atZone(ZoneId.systemDefault())),
-                              dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notAfter.time)
-                                                         .atZone(ZoneId.systemDefault()))
+                              dateTimeFormatter.format(
+                                Instant.ofEpochMilli(leafCertificate.notBefore.time)
+                                  .atZone(ZoneId.systemDefault())
+                              ),
+                              dateTimeFormatter.format(
+                                Instant.ofEpochMilli(leafCertificate.notAfter.time)
+                                  .atZone(ZoneId.systemDefault())
+                              )
                             )
                           )
                         )
@@ -590,7 +629,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                         .show()
                     }
                     // Certificate is in any other way invalid
-                    it is QuasselSecurityException.Certificate   -> {
+                    it is QuasselSecurityException.Certificate -> {
                       MaterialDialog.Builder(this)
                         .title(R.string.label_error_certificate)
                         .content(
@@ -679,45 +718,62 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
               }
             }
           }
+
           is Error.ConnectionError -> {
             it.throwable.let {
               val cause = it.cause
               when {
-                it is UnknownHostException         -> {
+                it is UnknownHostException -> {
                   val host = it.message?.replace("Host is unresolved: ", "")
 
-                  Toast.makeText(this,
-                                 getString(R.string.label_error_unknown_host, host),
-                                 Toast.LENGTH_LONG).show()
+                  Toast.makeText(
+                    this,
+                    getString(R.string.label_error_unknown_host, host),
+                    Toast.LENGTH_LONG
+                  ).show()
                 }
-                it is ProtocolVersionException     -> {
+
+                it is ProtocolVersionException -> {
                   val protocolVersion: Int = it.protocol.version.toInt()
-                  Toast.makeText(this,
-                                 getString(R.string.label_error_invalid_protocol_version,
-                                           protocolVersion),
-                                 Toast.LENGTH_LONG).show()
+                  Toast.makeText(
+                    this,
+                    getString(
+                      R.string.label_error_invalid_protocol_version,
+                      protocolVersion
+                    ),
+                    Toast.LENGTH_LONG
+                  ).show()
                 }
+
                 it is ConnectException &&
-                cause is libcore.io.ErrnoException -> {
+                  cause is libcore.io.ErrnoException -> {
                   val errorCode = OsConstants.errnoName(cause.errno)
                   val errorName = OsConstants.strerror(cause.errno)
 
-                  Toast.makeText(this,
-                                 getString(R.string.label_error_connection, errorName, errorCode),
-                                 Toast.LENGTH_LONG).show()
+                  Toast.makeText(
+                    this,
+                    getString(R.string.label_error_connection, errorName, errorCode),
+                    Toast.LENGTH_LONG
+                  ).show()
                 }
+
                 it is ConnectException && cause is ErrnoException -> {
                   val errorCode = OsConstants.errnoName(cause.errno)
                   val errorName = OsConstants.strerror(cause.errno)
 
-                  Toast.makeText(this,
-                                 getString(R.string.label_error_connection, errorName, errorCode),
-                                 Toast.LENGTH_LONG).show()
+                  Toast.makeText(
+                    this,
+                    getString(R.string.label_error_connection, errorName, errorCode),
+                    Toast.LENGTH_LONG
+                  ).show()
                 }
-                else                               -> {
-                  Toast.makeText(this,
-                                 getString(R.string.label_error_connection_closed),
-                                 Toast.LENGTH_LONG).show()
+
+                else -> {
+                  Toast.makeText(
+                    this,
+                    getString(R.string.label_error_connection_closed),
+                    Toast.LENGTH_LONG
+                  ).show()
                 }
               }
             }
@@ -734,8 +790,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       .observe(this, Observer {
         if (connectedAccount != accountId) {
           if (resources.getBoolean(R.bool.buffer_drawer_exists) &&
-              chatViewModel.bufferId.safeValue == BufferId.MAX_VALUE &&
-              !restoredDrawerState) {
+            chatViewModel.bufferId.safeValue == BufferId.MAX_VALUE &&
+            !restoredDrawerState
+          ) {
             binding.drawerLayout.openDrawer(GravityCompat.START)
           }
           connectedAccount = accountId
@@ -770,7 +827,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
     binding.layoutMain.connectionStatus.setOnClickListener {
       if (modelHelper.connectionProgress.value?.first == ConnectionState.CONNECTED
-        && modelHelper.deceptiveNetwork.value == true) {
+        && modelHelper.deceptiveNetwork.value == true
+      ) {
         DeceptiveNetworkDialog.Builder(this)
           .message(R.string.deceptive_network_freenode)
           .show()
@@ -789,13 +847,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
         val (state, progress, max) = connection
         when (state) {
           ConnectionState.DISCONNECTED,
-          ConnectionState.CLOSED     -> {
+          ConnectionState.CLOSED -> {
             binding.layoutMain.layoutToolbar.progressBar.visibility = View.INVISIBLE
 
             binding.layoutMain.connectionStatus.icon.setImageResource(R.drawable.ic_disconnected)
             binding.layoutMain.connectionStatus.setMode(WarningBarView.MODE_ICON)
             binding.layoutMain.connectionStatus.setText(getString(R.string.label_status_disconnected))
           }
+
           ConnectionState.CONNECTING -> {
             binding.layoutMain.layoutToolbar.progressBar.visibility = View.VISIBLE
             binding.layoutMain.layoutToolbar.progressBar.isIndeterminate = true
@@ -803,14 +862,16 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
             binding.layoutMain.connectionStatus.setMode(WarningBarView.MODE_PROGRESS)
             binding.layoutMain.connectionStatus.setText(getString(R.string.label_status_connecting))
           }
-          ConnectionState.HANDSHAKE  -> {
+
+          ConnectionState.HANDSHAKE -> {
             binding.layoutMain.layoutToolbar.progressBar.visibility = View.VISIBLE
             binding.layoutMain.layoutToolbar.progressBar.isIndeterminate = true
 
             binding.layoutMain.connectionStatus.setMode(WarningBarView.MODE_PROGRESS)
             binding.layoutMain.connectionStatus.setText(getString(R.string.label_status_handshake))
           }
-          ConnectionState.INIT       -> {
+
+          ConnectionState.INIT -> {
             binding.layoutMain.layoutToolbar.progressBar.visibility = View.VISIBLE
             // Show indeterminate when no progress has been made yet
             binding.layoutMain.layoutToolbar.progressBar.isIndeterminate = progress == 0 || max == 0
@@ -820,7 +881,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
             binding.layoutMain.connectionStatus.setMode(WarningBarView.MODE_PROGRESS)
             binding.layoutMain.connectionStatus.setText(getString(R.string.label_status_init))
           }
-          ConnectionState.CONNECTED  -> {
+
+          ConnectionState.CONNECTED -> {
             binding.layoutMain.layoutToolbar.progressBar.visibility = View.INVISIBLE
             if (deceptive && appearanceSettings.deceptiveNetworks) {
               binding.layoutMain.connectionStatus.setMode(WarningBarView.MODE_ICON)
@@ -839,13 +901,17 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       if (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
         binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END)
       } else {
-        binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END)
+        binding.drawerLayout.setDrawerLockMode(
+          DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
+          GravityCompat.END
+        )
       }
 
       invalidateOptionsMenu()
     })
 
-    editorBottomSheet = DragInterceptBottomSheetBehavior.from(binding.root.findViewById(R.id.fragment_chatline))
+    editorBottomSheet =
+      DragInterceptBottomSheetBehavior.from(binding.root.findViewById(R.id.fragment_chatline))
     editorBottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
     chatlineFragment?.panelSlideListener?.let(editorBottomSheet::setBottomSheetCallback)
 
@@ -861,8 +927,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
     })
 
-    combineLatest(modelHelper.allBuffers,
-                  modelHelper.chat.chatToJoin).map { (buffers, chatToJoinOptional) ->
+    combineLatest(
+      modelHelper.allBuffers,
+      modelHelper.chat.chatToJoin
+    ).map { (buffers, chatToJoinOptional) ->
       val chatToJoin = chatToJoinOptional.orNull()
       if (chatToJoin == null) {
         emptyList()
@@ -871,8 +939,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
         listOfNotNull(buffers.find {
           it.networkId == networkId &&
-          it.bufferName == channel &&
-          it.type.hasFlag(Buffer_Type.ChannelBuffer)
+            it.bufferName == channel &&
+            it.type.hasFlag(Buffer_Type.ChannelBuffer)
         })
       }
     }.filter {
@@ -882,6 +950,28 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
         launch(this, bufferId = info.bufferId)
       }
     }
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+      registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
+        if (isGranted) {
+          if (modelHelper.connectionProgress.value?.first == ConnectionState.CONNECTED) {
+            notificationBackend.showConnectedNotifications()
+          }
+        }
+      }
+
+      if (ActivityCompat.checkSelfPermission(
+          applicationContext,
+          Manifest.permission.POST_NOTIFICATIONS
+        ) != PackageManager.PERMISSION_GRANTED
+      ) {
+        ActivityCompat.requestPermissions(
+          this,
+          arrayOf(Manifest.permission.POST_NOTIFICATIONS),
+          1
+        )
+      }
+    }
   }
 
   override fun onNewIntent(intent: Intent?) {
@@ -938,7 +1028,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     chatViewModel.onSaveInstanceState(outState)
 
     outState.putLong(KEY_CONNECTED_ACCOUNT, connectedAccount.id)
-    outState.putBoolean(KEY_OPEN_DRAWER_START, binding.drawerLayout.isDrawerOpen(GravityCompat.START))
+    outState.putBoolean(
+      KEY_OPEN_DRAWER_START,
+      binding.drawerLayout.isDrawerOpen(GravityCompat.START)
+    )
     outState.putBoolean(KEY_OPEN_DRAWER_END, binding.drawerLayout.isDrawerOpen(GravityCompat.END))
   }
 
@@ -959,29 +1052,33 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
   override fun onCreateOptionsMenu(menu: Menu?): Boolean {
     val nickCountDrawableSize = resources.getDimensionPixelSize(R.dimen.size_nick_count)
-    val nickCountDrawableColor = binding.layoutMain.layoutToolbar.toolbar.context.theme.styledAttributes(
-      androidx.appcompat.R.attr.colorControlNormal
-    ) { getColor(0, 0) }
+    val nickCountDrawableColor =
+      binding.layoutMain.layoutToolbar.toolbar.context.theme.styledAttributes(
+        androidx.appcompat.R.attr.colorControlNormal
+      ) { getColor(0, 0) }
 
     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)
+        bufferData?.info?.type?.hasFlag(Buffer_Type.QueryBuffer) ?: false)
     menu?.retint(binding.layoutMain.layoutToolbar.toolbar.context)
     menu?.findItem(R.id.action_nicklist)?.icon = NickCountDrawable(
       bufferData?.userCount ?: 0,
       nickCountDrawableSize,
-      nickCountDrawableColor)
+      nickCountDrawableColor
+    )
     return super.onCreateOptionsMenu(menu)
   }
 
   override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
-    android.R.id.home           -> {
+    android.R.id.home -> {
       drawerToggle.onOptionsItemSelected(item)
     }
-    R.id.action_nicklist        -> {
+
+    R.id.action_nicklist -> {
       if (binding.drawerLayout.isDrawerVisible(GravityCompat.END)) {
         binding.drawerLayout.closeDrawer(GravityCompat.END)
       } else {
@@ -989,6 +1086,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
       true
     }
+
     R.id.action_filter_messages -> {
       runInBackground {
         chatViewModel.bufferId { buffer ->
@@ -1060,23 +1158,28 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
       true
     }
-    R.id.action_core_settings   -> {
+
+    R.id.action_core_settings -> {
       CoreSettingsActivity.launch(this)
       true
     }
+
     R.id.action_client_settings -> {
       ClientSettingsActivity.launch(this)
       true
     }
-    R.id.action_about           -> {
+
+    R.id.action_about -> {
       AboutActivity.launch(this)
       true
     }
-    R.id.action_disconnect      -> {
+
+    R.id.action_disconnect -> {
       disconnect()
       true
     }
-    else                        -> super.onOptionsItemSelected(item)
+
+    else -> super.onOptionsItemSelected(item)
   }
 
   override fun onBackPressed() {
@@ -1159,16 +1262,18 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       accountId: Long? = null,
       forceJoin: Boolean? = null
     ) = context.startActivity(
-      intent(context,
-             sharedText,
-             autoCompleteText,
-             autoCompleteSuffix,
-             channel,
-             nickName,
-             networkId,
-             bufferId,
-             accountId,
-             forceJoin)
+      intent(
+        context,
+        sharedText,
+        autoCompleteText,
+        autoCompleteSuffix,
+        channel,
+        nickName,
+        networkId,
+        bufferId,
+        accountId,
+        forceJoin
+      )
     )
 
     fun intent(
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/PreferenceDialogFragmentCompat.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/PreferenceDialogFragmentCompat.java
index 33678e3214ed5278b69e95f98f608e01d954a3ab..aad839cd56fd581022cc518d55076dd51c8c10b3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/PreferenceDialogFragmentCompat.java
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/settings/PreferenceDialogFragmentCompat.java
@@ -233,8 +233,7 @@ public abstract class PreferenceDialogFragmentCompat extends DialogFragment impl
       return null;
     }
 
-    LayoutInflater inflater = LayoutInflater.from(context);
-    return inflater.inflate(resId, null);
+    return getLayoutInflater().inflate(resId, null);
   }
 
   /**
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
index 340e32d88d881278fb35daf342f57dde52166e30..810707145e3e56db582134cd55d232903c215f00 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
@@ -53,7 +53,6 @@ class ShadowView : View {
     initialize(context, attrs, defStyleAttr, 0)
   }
 
-  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
   constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) :
     super(context, attrs, defStyleAttr, defStyleRes) {
     initialize(context, attrs, defStyleAttr, defStyleRes)
diff --git a/app/src/main/res/drawable/ic_monochrome.xml b/app/src/main/res/drawable/ic_monochrome.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3970098109d3b25a95a5879961ef2f2e0d9c99a0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_monochrome.xml
@@ -0,0 +1,26 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2023 Janne Mareike Koschinski
+  Copyright (c) 2023 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/>.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="48dp"
+  android:height="48dp"
+  android:viewportWidth="108"
+  android:viewportHeight="108">
+  <path android:fillColor="#ffffff" android:pathData="M27 54a 27,27 0 1,0 54,0 h-7 a20,20 0 0,1 -40,0 a20,20 0 0,1 40,0 h7 a 27,27 0 1,0 -54,0 M57.34 62.84 a 5.5,5.5 0 1,0 11,0 a 5.5,5.5 0 1,0 -11,0" />
+</vector>
diff --git a/app/src/main/res/mipmap-v26/ic_launcher.xml b/app/src/main/res/mipmap-v26/ic_launcher.xml
index 102979fec863ecb7857a936ed4a4a75992eb13b3..270925f9267f5575dfdf6e21058b22697c2ce3a6 100644
--- a/app/src/main/res/mipmap-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-v26/ic_launcher.xml
@@ -20,4 +20,5 @@
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
   <background android:drawable="@drawable/ic_launcher_background" />
   <foreground android:drawable="@mipmap/ic_launcher_foreground" />
+  <monochrome android:drawable="@drawable/ic_monochrome" />
 </adaptive-icon>
diff --git a/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts
deleted file mode 100644
index 01861dc9958065116e74f74d50c65d4022cd8624..0000000000000000000000000000000000000000
--- a/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts
+++ /dev/null
@@ -1,5 +0,0 @@
-repositories {
-  mavenCentral()
-  google()
-  maven(url = "https://jitpack.io")
-}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7924f651afe3004fb764356cd3b46ccbf82d6882..ec50abdf6572701455fe72ae7128a9c24d147d93 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -62,8 +62,10 @@ glide-core = { module = "com.github.bumptech.glide:glide", version.ref = "glide"
 glide-recyclerview = { module = "com.github.bumptech.glide:recyclerview-integration", version.ref = "glide" }
 
 gson = { module = "com.google.code.gson:gson", version = "2.9.0" }
-junit = { module = "junit:junit", version = "4.13.2" }
-kotlinpoet = { module = "com.squareup:kotlinpoet", version = "1.11.0" }
+hamcrest = { module = "org.hamcrest:hamcrest", version = "2.2" }
+junit-api = { module = "org.junit.jupiter:junit-jupiter-engine", version = "5.10.0" }
+junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version = "5.10.0" }
+kotlinpoet = { module = "com.squareup:kotlinpoet", version = "1.12.0" }
 ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
 leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version = "2.8.1" }
 
diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts
index 7607e029e3f1281f35f9d3b2a85c701a68aee882..31f4170217288ff7bed91a67994881e8c8cc3f8b 100644
--- a/lib/build.gradle.kts
+++ b/lib/build.gradle.kts
@@ -30,5 +30,7 @@ dependencies {
   implementation(project(":invokerannotations"))
   ksp(project(":invokergenerator"))
 
-  testImplementation(libs.junit)
+  testImplementation(libs.hamcrest)
+  testImplementation(libs.junit.api)
+  testRuntimeOnly(libs.junit.engine)
 }
diff --git a/lib/src/test/java/de/kuschku/libquassel/integration/AliasManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/integration/AliasManagerTest.kt
index d0cdd9489aca7417cc14d8f634b2568e3a8998a6..9baad0406151ab38699d2cefefef49188221e0f4 100644
--- a/lib/src/test/java/de/kuschku/libquassel/integration/AliasManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/integration/AliasManagerTest.kt
@@ -26,16 +26,16 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
 import de.kuschku.libquassel.util.TestSession
 import de.kuschku.libquassel.util.setupTestSession
 import de.kuschku.libquassel.util.with
-import org.junit.Assert.assertEquals
-import org.junit.Before
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
 
 class AliasManagerTest {
   lateinit var session: TestSession
   lateinit var channelBuffer: BufferInfo
   lateinit var queryBuffer: BufferInfo
 
-  @Before
+  @BeforeEach
   fun setUp() {
     session = setupTestSession()
     session.aliasManager.setAliasList(listOf(
diff --git a/lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt b/lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt
index 2c46068fe8e4f9f0e91cd3c4e8fe37805fef3a3f..7e061446558e227963f105dc4b2b542a3493c09e 100644
--- a/lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt
@@ -26,13 +26,13 @@ import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.util.TestSession
 import de.kuschku.libquassel.util.setupTestSession
 import de.kuschku.libquassel.util.with
-import org.junit.Before
-import org.junit.Test
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
 
 class BufferViewConfigTest {
   lateinit var session: TestSession
 
-  @Before
+  @BeforeEach
   fun setUp() {
     session = setupTestSession()
   }
diff --git a/lib/src/test/java/de/kuschku/libquassel/integration/SampleIntegrationTest.kt b/lib/src/test/java/de/kuschku/libquassel/integration/SampleIntegrationTest.kt
index 0f5b8680bd87413ab0846fc3f9f3144469726b04..f2f56ac43f5ec8e51ab950ccf9bf3b74ad8b314b 100644
--- a/lib/src/test/java/de/kuschku/libquassel/integration/SampleIntegrationTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/integration/SampleIntegrationTest.kt
@@ -20,11 +20,11 @@
 package de.kuschku.libquassel.integration
 
 import de.kuschku.libquassel.protocol.NetworkId
-import de.kuschku.libquassel.protocol.QuasselType
 import de.kuschku.libquassel.protocol.QVariant_
 import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
 import de.kuschku.libquassel.util.withTestSession
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class SampleIntegrationTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/integration/SignedIdNameTests.kt b/lib/src/test/java/de/kuschku/libquassel/integration/SignedIdNameTests.kt
index 21b76ee33d3ee5b2a51e6e64762374126be3c813..1e662ff344777ecf75cc4a85b3149fb26963fd36 100644
--- a/lib/src/test/java/de/kuschku/libquassel/integration/SignedIdNameTests.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/integration/SignedIdNameTests.kt
@@ -22,7 +22,7 @@ package de.kuschku.libquassel.integration
 import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.util.withTestSession
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 /*
  * When implementing SignedIds properly for the first time, we noticed that they were used in
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BoolSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BoolSerializerTest.kt
index 743ea0a65074f522ea35e9edc34cd4dd4f1dde98..43f663a5bec1bd7b2e0bfd9b1f3ea0c99e00879a 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BoolSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BoolSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class BoolSerializerTest {
   @Test
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 803bc62f6e82dee0be7c0968c867b3ec16ea603b..02b6ee53dd7b5c81c71c522621473e79308057b1 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
@@ -25,8 +25,8 @@ import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class BufferInfoSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteArraySerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteArraySerializerTest.kt
index 548f340efbbce7f8b1b722216ba2000db5fbd971..850c2acfb5016c6fd1fd12b6184a9392c1a1413b 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteArraySerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteArraySerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertArrayEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertArrayEquals
+import org.junit.jupiter.api.Test
 import java.nio.ByteBuffer
 
 class ByteArraySerializerTest {
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteSerializerTest.kt
index 81850e3201c0d965f121fcb2eeb413c96d431d4d..22ba51ce9dc58fec86400a7da39a63ad774c34c5 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ByteSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
 
 class ByteSerializerTest {
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/CharSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/CharSerializerTest.kt
index 0f6ebaa3609831881d8a18a6a439455fc997dfc3..37cf6d7a0553a2d68a6c65dd669665f8f30e3df1 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/CharSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/CharSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class CharSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializerTest.kt
index d8cd85655fc85c0fb3029a37373f86f7c03b046a..d3cc81c7c83f0c1a139804dd9e0693e4f9b0952f 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializerTest.kt
@@ -21,9 +21,13 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.threeten.bp.*
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.threeten.bp.Instant
+import org.threeten.bp.LocalDateTime
+import org.threeten.bp.Month
+import org.threeten.bp.ZoneId
+import org.threeten.bp.ZoneOffset
 
 class DateTimeSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/HostAddressSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/HostAddressSerializerTest.kt
index a6963e2d89bc1caddb27b944d189654c0fd7e2d5..af8a8e665a9d78d9fac6d28910786b3df7dc4c51 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/HostAddressSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/HostAddressSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 import java.net.InetAddress
 
 class HostAddressSerializerTest {
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/IntSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/IntSerializerTest.kt
index e2d3d5d426c84e91f28d4c100d08552c5bafab39..6300ba646d1bd4793726f736461c72b1a8597aba 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/IntSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/IntSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class IntSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/LongSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/LongSerializerTest.kt
index dd5bf9b59cb7adc3ca50fca279f3ef55475d9b26..9b4aa673caa694d7f559fedb4c43e674d38945de 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/LongSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/LongSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class LongSerializerTest {
   @Test
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 8ad3b0bdf8968feb34ed1a6833eb010c23e89359..f8364af6b751d0261501582834fa87c1d4a55385 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
@@ -19,13 +19,19 @@
 
 package de.kuschku.libquassel.protocol.primitive.serializer
 
-import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.Message_Flag
+import de.kuschku.libquassel.protocol.Message_Type
+import de.kuschku.libquassel.protocol.MsgId
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 import org.threeten.bp.Instant
 
 class MessageSerializerTest {
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ProtocolInfoSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ProtocolInfoSerializerTest.kt
index ff55d5dc0d23aa0213d4c384e2f997a2f3a6b447..b16c6ea8df17084ae0f0811c34982e6b0d8e5b87 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ProtocolInfoSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ProtocolInfoSerializerTest.kt
@@ -24,8 +24,8 @@ import de.kuschku.libquassel.protocol.Protocol_Feature
 import de.kuschku.libquassel.quassel.ProtocolInfo
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class ProtocolInfoSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ShortSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ShortSerializerTest.kt
index f72ed3aad12b72269bf0bab74b556cca77aba6c0..c5115aecbf5b4ee87a6ce3b78521458753bc6bf8 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ShortSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/ShortSerializerTest.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
 
 class ShortSerializerTest {
diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializerTest.kt
index 5b07c0bf5f255d2928938927a3b49579a9fe00f4..8c52b7c24e9d6464ee092eb4e8603e79d8981dd0 100644
--- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializerTest.kt
@@ -23,9 +23,9 @@ import de.kuschku.libquassel.util.deserialize
 import de.kuschku.libquassel.util.roundTrip
 import org.hamcrest.BaseMatcher
 import org.hamcrest.Description
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
-import org.junit.Test
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class StringSerializerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt
index fa9bc86c147159e940b1829c5ef8f8854df9ecf6..00c37a5dae12a48980b104c8dc755dbe796bdde2 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/BufferTypeTest.kt
@@ -21,67 +21,69 @@ package de.kuschku.libquassel.quassel
 
 import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.util.flag.hasFlag
-import org.junit.Assert
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
 
 class BufferTypeTest {
   @Test
   fun testHasFlag() {
-    Assert.assertTrue(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.StatusBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.QueryBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.ChannelBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.GroupBuffer))
+    assertTrue(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.StatusBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.QueryBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.ChannelBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.StatusBuffer).hasFlag(Buffer_Type.GroupBuffer))
 
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.StatusBuffer))
-    Assert.assertTrue(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.QueryBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.ChannelBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.GroupBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.StatusBuffer))
+    assertTrue(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.QueryBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.ChannelBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.QueryBuffer).hasFlag(Buffer_Type.GroupBuffer))
 
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.StatusBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.QueryBuffer))
-    Assert.assertTrue(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.ChannelBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.GroupBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.StatusBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.QueryBuffer))
+    assertTrue(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.ChannelBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.ChannelBuffer).hasFlag(Buffer_Type.GroupBuffer))
 
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.StatusBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.QueryBuffer))
-    Assert.assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.ChannelBuffer))
-    Assert.assertTrue(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.GroupBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.StatusBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.QueryBuffer))
+    assertFalse(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.ChannelBuffer))
+    assertTrue(Buffer_Type.of(Buffer_Type.GroupBuffer).hasFlag(Buffer_Type.GroupBuffer))
   }
 
   @Test
   fun testEnabledValues() {
-    Assert.assertEquals(
+    assertEquals(
       Buffer_Type.StatusBuffer,
       Buffer_Type.of(Buffer_Type.StatusBuffer).enabledValues().firstOrNull()
     )
-    Assert.assertEquals(
+    assertEquals(
       setOf(Buffer_Type.StatusBuffer),
       Buffer_Type.of(Buffer_Type.StatusBuffer).enabledValues()
     )
 
-    Assert.assertEquals(
+    assertEquals(
       Buffer_Type.QueryBuffer,
       Buffer_Type.of(Buffer_Type.QueryBuffer).enabledValues().firstOrNull()
     )
-    Assert.assertEquals(
+    assertEquals(
       setOf(Buffer_Type.QueryBuffer),
       Buffer_Type.of(Buffer_Type.QueryBuffer).enabledValues()
     )
 
-    Assert.assertEquals(
+    assertEquals(
       Buffer_Type.ChannelBuffer,
       Buffer_Type.of(Buffer_Type.ChannelBuffer).enabledValues().firstOrNull()
     )
-    Assert.assertEquals(
+    assertEquals(
       setOf(Buffer_Type.ChannelBuffer),
       Buffer_Type.of(Buffer_Type.ChannelBuffer).enabledValues()
     )
 
-    Assert.assertEquals(
+    assertEquals(
       Buffer_Type.GroupBuffer,
       Buffer_Type.of(Buffer_Type.GroupBuffer).enabledValues().firstOrNull()
     )
-    Assert.assertEquals(
+    assertEquals(
       setOf(Buffer_Type.GroupBuffer),
       Buffer_Type.of(Buffer_Type.GroupBuffer).enabledValues()
     )
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 3d2008a65f6b0ae99b576476eda5746bc72c739e..bb164e9d06360e71a3690b7f194b453e4e87f6d9 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
@@ -22,7 +22,7 @@ package de.kuschku.libquassel.quassel.syncables
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class AliasManagerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferSyncerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferSyncerTest.kt
index 2a64f7c8b420d3c622cb220097366ff243582f16..165fcd9d6b6fbaee2de4d43c4dce00601f76ccd7 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferSyncerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferSyncerTest.kt
@@ -25,7 +25,7 @@ import de.kuschku.libquassel.protocol.MsgId
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class BufferSyncerTest {
   @Test
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 94eea57ffdfee235b2fbb67853ae44b448283aa0..3299d25182cbd88f2e3c78be56c4cbda9e519182 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
@@ -24,8 +24,12 @@ 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.*
-import org.junit.Test
+import de.kuschku.libquassel.util.randomBoolean
+import de.kuschku.libquassel.util.randomInt
+import de.kuschku.libquassel.util.randomOf
+import de.kuschku.libquassel.util.randomString
+import de.kuschku.libquassel.util.roundTrip
+import org.junit.jupiter.api.Test
 
 class BufferViewConfigTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewManagerTest.kt
index 258eaf969c6667366e33cf41eb263a89af1603c5..3cb6fa9b2d7a470d1368a2a56400a6389ee094a2 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewManagerTest.kt
@@ -22,7 +22,7 @@ package de.kuschku.libquassel.quassel.syncables
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class BufferViewManagerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManagerTest.kt
index 84dbbce94b004586108e0e0107fa5ae8e97becbd..9737bfab18b2cc8ed54ef3c303efc0f46dc38e38 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManagerTest.kt
@@ -22,8 +22,12 @@ package de.kuschku.libquassel.quassel.syncables
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
 import de.kuschku.libquassel.quassel.syncables.interfaces.IHighlightRuleManager
 import de.kuschku.libquassel.session.SignalProxy
-import de.kuschku.libquassel.util.*
-import org.junit.Test
+import de.kuschku.libquassel.util.randomBoolean
+import de.kuschku.libquassel.util.randomInt
+import de.kuschku.libquassel.util.randomOf
+import de.kuschku.libquassel.util.randomString
+import de.kuschku.libquassel.util.roundTrip
+import org.junit.jupiter.api.Test
 
 class HighlightRuleManagerTest {
   @Test
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 93da54521606ab6bc4e2c007996d09aaf12836f8..ea5ee6e595d4ae1e6ed6976da4f424feb7ee2fe7 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
@@ -25,7 +25,7 @@ import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.randomInt
 import de.kuschku.libquassel.util.randomString
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class IdentityTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManagerTest.kt
index 1d4e58da4177b3750e5abd0f1f5b43b408815873..e29d392cb6cb0c4d4241e0f99bc975b3afc64826 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManagerTest.kt
@@ -25,7 +25,7 @@ import de.kuschku.libquassel.util.randomBoolean
 import de.kuschku.libquassel.util.randomOf
 import de.kuschku.libquassel.util.randomString
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
 class IgnoreListManagerTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkConfigTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkConfigTest.kt
index ff981a4fac1dc0bb2707432fe10dc125620ace50..a1a3091a9546fefd86ba43c04631f26758cb2877 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkConfigTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkConfigTest.kt
@@ -24,7 +24,7 @@ import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.randomBoolean
 import de.kuschku.libquassel.util.randomInt
 import de.kuschku.libquassel.util.roundTrip
-import org.junit.Test
+import org.junit.jupiter.api.Test
 import kotlin.math.abs
 
 class NetworkConfigTest {
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 c0636744335e055099cd6dd6ec4b209930afeb3d..f0be3e52e9dc340484a72610e8ed12693fbbc34a 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
@@ -24,8 +24,16 @@ 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
-import de.kuschku.libquassel.util.*
-import org.junit.Test
+import de.kuschku.libquassel.util.randomBoolean
+import de.kuschku.libquassel.util.randomCharset
+import de.kuschku.libquassel.util.randomInstant
+import de.kuschku.libquassel.util.randomInt
+import de.kuschku.libquassel.util.randomOf
+import de.kuschku.libquassel.util.randomString
+import de.kuschku.libquassel.util.randomUInt
+import de.kuschku.libquassel.util.randomUShort
+import de.kuschku.libquassel.util.roundTrip
+import org.junit.jupiter.api.Test
 
 class NetworkTest {
   @Test
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 517f8239663560e538cba4a5cfcdc8714c6aa245..d2727cbde18dc1d57c232619ba853d6f443ddd56 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
@@ -23,8 +23,8 @@ 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
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class INetworkInfoTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/ExpressionMatchTest.kt b/lib/src/test/java/de/kuschku/libquassel/util/ExpressionMatchTest.kt
index 86aba750fbec3dbba4d889353ef0ab852ee14b81..e422365a943a2249add7a2a833d2300f38673b3e 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/ExpressionMatchTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/ExpressionMatchTest.kt
@@ -19,8 +19,10 @@
 
 package de.kuschku.libquassel.util
 
-import org.junit.Assert.*
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
 
 class ExpressionMatchTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/TestSession.kt b/lib/src/test/java/de/kuschku/libquassel/util/TestSession.kt
index aec1e47e8b26a37632e1be49943e54b5b21d58c7..31ed64d7ec1f6d458af3458ef9fcbb573f033478 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/TestSession.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/TestSession.kt
@@ -56,7 +56,7 @@ import de.kuschku.libquassel.session.ProtocolHandler
 import io.reactivex.Observable
 import io.reactivex.subjects.BehaviorSubject
 import io.reactivex.subjects.PublishSubject
-import org.junit.Assert.assertTrue
+import org.junit.jupiter.api.Assertions.assertTrue
 import javax.net.ssl.SSLSession
 
 class TestSession : ProtocolHandler({ throw it }), ISession {
@@ -79,35 +79,35 @@ class TestSession : ProtocolHandler({ throw it }), ISession {
         it.className == target.className
       }
       assertTrue(
-        "SYNC No calls were made on objects of type ${target.className}",
-        matchingTargetTypes.isNotEmpty()
+        matchingTargetTypes.isNotEmpty(),
+        "SYNC No calls were made on objects of type ${target.className}"
       )
       val matchingTargets = matchingTargetTypes.filter {
         it.objectName == target.objectName
       }
       assertTrue(
+        matchingTargets.isNotEmpty(),
         if (matchingTargetTypes.isNotEmpty()) {
           "SYNC No calls were made on ${target.className}:${target.objectName}, instead only on:\n  ${matchingTargetTypes.map { "${it.className}:${it.objectName}" }.joinToString(
             "\n  ")}"
         } else {
           "SYNC No calls were made on ${target.className}:${target.objectName}"
-        },
-        matchingTargets.isNotEmpty()
+        }
       )
 
       val matchingNames = matchingTargets.filter {
         it.slotName == slotName
       }
       assertTrue(
-        "SYNC ${target.className}:${target.objectName}:$slotName() was never called",
-        matchingNames.isNotEmpty()
+        matchingNames.isNotEmpty(),
+        "SYNC ${target.className}:${target.objectName}:$slotName() was never called"
       )
       if (!params.isNullOrEmpty()) {
         val calledParams = matchingNames.map(SignalProxyMessage.SyncMessage::params)
         assertTrue(
+          calledParams.contains(params),
           "SYNC ${target.className}:${target.objectName}:$slotName was called with the wrong parameters:\nExpected:\n  $params\nActual:\n  ${calledParams.joinToString(
-            "\n  ")}",
-          calledParams.contains(params)
+            "\n  ")}"
         )
       }
     }
@@ -118,15 +118,15 @@ class TestSession : ProtocolHandler({ throw it }), ISession {
         it.slotName == slotName
       }
       assertTrue(
-        "RPC $slotName() was not called",
-        matchingNames.isNotEmpty()
+        matchingNames.isNotEmpty(),
+        "RPC $slotName() was not called"
       )
       if (!params.isNullOrEmpty()) {
         val calledParams = matchingNames.map(SignalProxyMessage.RpcCall::params)
         assertTrue(
+          calledParams.contains(params),
           "RPC $slotName was called with the wrong parameters:\nExpected:\n  $params\nActual:\n  ${calledParams.joinToString(
-            "\n  ")}",
-          calledParams.contains(params)
+            "\n  ")}"
         )
       }
     }
@@ -136,16 +136,16 @@ class TestSession : ProtocolHandler({ throw it }), ISession {
         it.className == className
       }
       assertTrue(
-        "InitRequest No data was requested for objects of type $className",
-        matchingType.isNotEmpty()
+        matchingType.isNotEmpty(),
+        "InitRequest No data was requested for objects of type $className"
       )
       val matchingCalls = matchingType.filter {
         it.objectName == objectName
       }
       assertTrue(
+        matchingCalls.isNotEmpty(),
         "InitRequest No data was requested for object $className:$objectName, instead only for:\n  ${matchingType.map { "${it.className}:${it.objectName}" }.joinToString(
-          "\n  ")}",
-        matchingCalls.isNotEmpty()
+          "\n  ")}"
       )
     }
   }
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/irc/HostmaskHelperTest.kt b/lib/src/test/java/de/kuschku/libquassel/util/irc/HostmaskHelperTest.kt
index a49ccbd89685ee2b0d9704ef049eba92b9369fd7..04d69d2cfaf1ea5d8f24671932ca793e9e8f0b5e 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/irc/HostmaskHelperTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/irc/HostmaskHelperTest.kt
@@ -19,8 +19,8 @@
 
 package de.kuschku.libquassel.util.irc
 
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class HostmaskHelperTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/nio/WrappedChannelTest.kt b/lib/src/test/java/de/kuschku/libquassel/util/nio/WrappedChannelTest.kt
index ff5bbc08509ee1fd775070c2cdde561a8fffe9ec..7cc4b940bce864ea6a09311339454608a3199c5e 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/nio/WrappedChannelTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/nio/WrappedChannelTest.kt
@@ -20,8 +20,8 @@
 package de.kuschku.libquassel.util.nio
 
 import de.kuschku.libquassel.util.nio.WrappedChannel.Companion.selectBestTlsVersion
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class WrappedChannelTest {
   @Test
diff --git a/lib/src/test/java/de/kuschku/libquassel/util/rxjava/ReusableUnicastSubjectTest.kt b/lib/src/test/java/de/kuschku/libquassel/util/rxjava/ReusableUnicastSubjectTest.kt
index 7310ea8ded4a28beadc4830932390950c189bc52..01d15162c230b97a1aa7b76f494433f4b27b8858 100644
--- a/lib/src/test/java/de/kuschku/libquassel/util/rxjava/ReusableUnicastSubjectTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/util/rxjava/ReusableUnicastSubjectTest.kt
@@ -19,8 +19,8 @@
 
 package de.kuschku.libquassel.util.rxjava
 
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class ReusableUnicastSubjectTest {
   @Test
diff --git a/lint.xml b/lint.xml
index eab67bd5e6f29d10e42082945d8a9195c76e4777..fe9afed566f6760def8d201ac8492fb1c2ea293f 100644
--- a/lint.xml
+++ b/lint.xml
@@ -33,7 +33,8 @@
   <issue id="Overdraw" severity="ignore" />
 
   <!-- Can’t request a translation without a release, can’t release without translation -->
-  <issue id="MissingTranslation" severity="informational" />
+  <issue id="MissingTranslation" severity="ignore" />
+  <issue id="MissingQuantity" severity="ignore" />
   <!-- Because we don’t use app bundles and never will use them -->
   <issue id="AppBundleLocaleChanges" severity="informational" />
   <!-- Because this tries to apply english orthography to other locales -->
diff --git a/malheur/src/main/java/de/kuschku/malheur/collectors/ConfigurationCollector.kt b/malheur/src/main/java/de/kuschku/malheur/collectors/ConfigurationCollector.kt
index 01f5e2ba75b17f4f1a6878f3d64219350880113f..9842e4217f4599fc92012c850a212823cb4a0f95 100644
--- a/malheur/src/main/java/de/kuschku/malheur/collectors/ConfigurationCollector.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/collectors/ConfigurationCollector.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.malheur.collectors
 
+import android.annotation.SuppressLint
 import android.app.Application
 import android.content.res.Configuration
 import android.util.SparseArray
@@ -86,6 +87,7 @@ class ConfigurationCollector(private val application: Application) :
     }
   }
 
+  @SuppressLint("PrivateApi")
   override fun collect(context: CrashContext,
                        config: Boolean) = configurationFields.mapNotNull { info ->
     val field: Field? = Configuration::class.java.getDeclaredField(info.fieldName)
diff --git a/viewmodel/build.gradle.kts b/viewmodel/build.gradle.kts
index 11232147771aab3952f03d46fbf1ab6bed7dd8bc..1fb805a148678bce3bd6371d81eb374c4e8d3d86 100644
--- a/viewmodel/build.gradle.kts
+++ b/viewmodel/build.gradle.kts
@@ -47,5 +47,6 @@ dependencies {
     exclude(group = "org.threeten", module = "threetenbp")
   }
 
-  testImplementation(libs.junit)
+  testImplementation(libs.junit.api)
+  testRuntimeOnly(libs.junit.engine)
 }
diff --git a/viewmodel/src/test/java/de/kuschku/quasseldroid/viewmodel/ChatViewModelTest.kt b/viewmodel/src/test/java/de/kuschku/quasseldroid/viewmodel/ChatViewModelTest.kt
index 063d14a1d0dc324cbf4af4e4cb648e67d3cf6886..2a6112affcc85850d6db59ed71799db2665b7a8a 100644
--- a/viewmodel/src/test/java/de/kuschku/quasseldroid/viewmodel/ChatViewModelTest.kt
+++ b/viewmodel/src/test/java/de/kuschku/quasseldroid/viewmodel/ChatViewModelTest.kt
@@ -20,8 +20,8 @@
 package de.kuschku.quasseldroid.viewmodel
 
 import de.kuschku.quasseldroid.viewmodel.ChatViewModel.Companion.recentMessagesChangeInternal
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class ChatViewModelTest {
   @Test