diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8ed18a833e4206f7b9338d4c601b9fd978b21066..31da6db528c8d48784502e0a128175bd7fea8166 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -50,7 +50,7 @@ deploy:
   dependencies:
   - "build"
   script:
-  - "echo $S3_CONFIG > $HOME/.s3cfg"
+  - "echo $S3_CONFIG | base64 -d > $HOME/.s3cfg"
   - "export VERSION=$(ls *.apk)"
   - "s3cmd put $VERSION s3://releases/quasseldroid-ng/$VERSION"
   - "s3cmd cp s3://releases/quasseldroid-ng/$VERSION s3://releases/quasseldroid-ng/Quasseldroid-latest.apk"
diff --git a/README.md b/README.md
index ac716adf61eb2f861ee17851692529ed7712ae20..7a01f76c2e526163cfebab25c9f5e8a5a637bffe 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,8 @@ Authors of the previous version of Quasseldroid:
   GPLv3
 * [**Reactive Streams**](https://github.com/ReactiveX/RxJava)
   CC0
+* [**Retrofit**](https://square.github.io/retrofit/)
+  Apache-2.0
 * [**RxJava**](https://github.com/ReactiveX/RxJava)
   Apache-2.0
 * [**ThreeTen backport project**](http://www.threeten.org/threetenbp/)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ada6f42557d3598086076defc958b3568f11ad47..45bfb63576f41bb4911cae7904bd0fc9de551da8 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -36,6 +36,25 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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/>.
+ */
+
 import org.gradle.api.Project
 import java.io.FileInputStream
 import java.util.*
@@ -169,6 +188,8 @@ dependencies {
   implementation("org.jetbrains", "annotations", "16.0.1")
   implementation("com.google.code.gson", "gson", "2.8.2")
   implementation("commons-codec", "commons-codec", "1.11")
+  implementation("com.squareup.retrofit2", "retrofit", "2.4.0")
+  implementation("com.squareup.retrofit2", "converter-gson", "2.4.0")
   withVersion("8.8.1") {
     implementation("com.jakewharton", "butterknife", version)
     kapt("com.jakewharton", "butterknife-compiler", version)
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index a6839415a8537b7ed338f6be54601e4704cec5c7..5d4a7e48c3256ed0a4399a9a968d4887655b6311 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -41,4 +41,20 @@
 # Gson
 -dontnote com.google.gson.**
 # Dagger
--dontwarn com.google.errorprone.annotations.*
\ No newline at end of file
+-dontwarn com.google.errorprone.annotations.*
+# Retrofit
+# Platform calls Class.forName on types which do not exist on Android to determine platform.
+-dontnote retrofit2.Platform
+# Platform used when running on Java 8 VMs. Will not be used at runtime.
+-dontwarn retrofit2.Platform$Java8
+# Annotation used by Retrofit on Java 8 VMs
+-dontwarn javax.annotation.Nullable
+-dontwarn javax.annotation.ParametersAreNonnullByDefault
+-dontwarn javax.annotation.concurrent.GuardedBy
+# Retain generic type information for use by reflection by converters and adapters.
+-keepattributes Signature
+# Retain declared checked exceptions for use by a Proxy instance.
+-keepattributes Exceptions
+# Okio
+-dontwarn okio.**
+-dontwarn org.conscrypt.**
diff --git a/app/src/main/java/de/kuschku/quasseldroid/QuasseldroidGlideModule.kt b/app/src/main/java/de/kuschku/quasseldroid/QuasseldroidGlideModule.kt
index ee972ab5f23e2e8743dcadec7b6b6fa095441f3d..cea18d1376edea8207511c8c20b183472233eaee 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/QuasseldroidGlideModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/QuasseldroidGlideModule.kt
@@ -21,13 +21,44 @@ package de.kuschku.quasseldroid
 
 import android.content.Context
 import android.util.Log
+import com.bumptech.glide.Glide
 import com.bumptech.glide.GlideBuilder
+import com.bumptech.glide.Registry
 import com.bumptech.glide.annotation.GlideModule
+import com.bumptech.glide.load.model.ModelLoader
+import com.bumptech.glide.load.model.ModelLoaderFactory
+import com.bumptech.glide.load.model.MultiModelLoaderFactory
 import com.bumptech.glide.module.AppGlideModule
+import com.google.gson.GsonBuilder
+import de.kuschku.quasseldroid.util.avatars.MatrixApi
+import de.kuschku.quasseldroid.util.avatars.MatrixModelLoader
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.io.InputStream
 
 @GlideModule
 class QuasseldroidGlideModule : AppGlideModule() {
   override fun applyOptions(context: Context, builder: GlideBuilder) {
     if (!BuildConfig.DEBUG) builder.setLogLevel(Log.ERROR)
   }
+
+  override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
+    val matrixApi = Retrofit.Builder()
+      .baseUrl("https://matrix.org/")
+      .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
+      .build()
+      .create(MatrixApi::class.java)
+
+    registry.append(Avatar.MatrixAvatar::class.java,
+                    InputStream::class.java,
+                    object : ModelLoaderFactory<Avatar.MatrixAvatar, InputStream> {
+                      override fun build(
+                        multiFactory: MultiModelLoaderFactory): ModelLoader<Avatar.MatrixAvatar, InputStream> {
+                        return MatrixModelLoader(matrixApi)
+                      }
+
+                      override fun teardown() = Unit
+                    })
+  }
 }
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 0c14dc7e44d3b46da742f1c893bd929f2242492b..80456bf453a568563f63c952cc2227b196d89216 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
@@ -38,8 +38,8 @@ import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.settings.NotificationSettings
 import de.kuschku.quasseldroid.settings.Settings
-import de.kuschku.quasseldroid.util.AvatarHelper
 import de.kuschku.quasseldroid.util.NotificationMessage
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.getColorCompat
 import de.kuschku.quasseldroid.util.helper.loadWithFallbacks
 import de.kuschku.quasseldroid.util.helper.styledAttributes
diff --git a/app/src/main/java/de/kuschku/quasseldroid/settings/MessageSettings.kt b/app/src/main/java/de/kuschku/quasseldroid/settings/MessageSettings.kt
index 15aae626cb8409fd026c09302026019f3a91fffe..459dc2ea0222555fff894b1cd788c3712c20f754 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/settings/MessageSettings.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/settings/MessageSettings.kt
@@ -34,6 +34,7 @@ data class MessageSettings(
   val showAvatars: Boolean = true,
   val showIRCCloudAvatars: Boolean = false,
   val showGravatarAvatars: Boolean = false,
+  val showMatrixAvatars: Boolean = false,
   val largerEmoji: Boolean = false
 ) {
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt b/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
index 9a1730e64b3c743d85a0b3830a4546bb48453a78..997e99d8ee00696fb90c5104890423a3d8679ac8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
@@ -109,6 +109,10 @@ object Settings {
         context.getString(R.string.preference_show_gravatar_avatars_key),
         MessageSettings.DEFAULT.showGravatarAvatars
       ),
+      showMatrixAvatars = getBoolean(
+        context.getString(R.string.preference_show_matrix_avatars_key),
+        MessageSettings.DEFAULT.showMatrixAvatars
+      ),
       largerEmoji = getBoolean(
         context.getString(R.string.preference_larger_emoji_key),
         MessageSettings.DEFAULT.largerEmoji
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
index 755168760854bca03329da0d6161886ef2ecce36..b226ad3c9d550e6cd7d8f6e1762089bc0e0d4d7e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
@@ -37,7 +37,7 @@ import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
 import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper.Companion.IGNORED_CHARS
-import de.kuschku.quasseldroid.util.AvatarHelper
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
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 9d61428b0d8a1737a496558956ef296d73621811..44a0b934b975b588ee221ceb71fdf47311c3aa3b 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
@@ -33,7 +33,7 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
-import de.kuschku.quasseldroid.util.AvatarHelper
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.styledAttributes
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
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 89bcbe7cbdcd4e4c000c54835afc6bc8bbe26582..d66fb83c7094b611c2b89631412b296202902a28 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
@@ -53,11 +53,12 @@ import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.BacklogSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
-import de.kuschku.quasseldroid.util.AvatarHelper
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
 import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper
 import de.kuschku.quasseldroid.util.ui.SpanFormatter
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
 import io.reactivex.BackpressureStrategy
 import org.threeten.bp.ZoneId
 import org.threeten.bp.ZonedDateTime
@@ -402,14 +403,14 @@ class MessageListFragment : ServiceBoundFragment() {
       requireContext().resources.displayMetrics
     ).roundToInt()
 
-    val sizeProvider = FixedPreloadSizeProvider<List<String>>(avatarSize, avatarSize)
+    val sizeProvider = FixedPreloadSizeProvider<List<Avatar>>(avatarSize, avatarSize)
 
-    val preloadModelProvider = object : ListPreloader.PreloadModelProvider<List<String>> {
+    val preloadModelProvider = object : ListPreloader.PreloadModelProvider<List<Avatar>> {
       override fun getPreloadItems(position: Int) = listOfNotNull(
         adapter[position]?.content?.let { AvatarHelper.avatar(messageSettings, it, avatarSize) }
       )
 
-      override fun getPreloadRequestBuilder(item: List<String>) =
+      override fun getPreloadRequestBuilder(item: List<Avatar>) =
         GlideApp.with(this@MessageListFragment).loadWithFallbacks(item)?.override(avatarSize)
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
index bbe2414d1ef274eab22437ef7df69af9f4c7b855..d27fb4107665d60f7938d301b2571a9d3aeb0ad7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
@@ -38,7 +38,7 @@ import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
 import de.kuschku.quasseldroid.settings.MessageSettings
-import de.kuschku.quasseldroid.util.AvatarHelper
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.styledAttributes
 import de.kuschku.quasseldroid.util.helper.visibleIf
 import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
index 14a4c95ee7bfd0e03121ee7fdeb2f6e4a187876b..e7ef5a4e0c60094d3ad171232f7ad49f1990b2ea 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
@@ -48,13 +48,14 @@ import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoActivity
 import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper.Companion.IGNORED_CHARS
-import de.kuschku.quasseldroid.util.AvatarHelper
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.loadWithFallbacks
 import de.kuschku.quasseldroid.util.helper.styledAttributes
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
 import de.kuschku.quasseldroid.util.ui.TextDrawable
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
 import javax.inject.Inject
 
 class NickListFragment : ServiceBoundFragment() {
@@ -159,14 +160,14 @@ class NickListFragment : ServiceBoundFragment() {
       nickList.layoutManager.onRestoreInstanceState(getParcelable(KEY_STATE_LIST))
     }
 
-    val sizeProvider = FixedPreloadSizeProvider<List<String>>(avatarSize, avatarSize)
+    val sizeProvider = FixedPreloadSizeProvider<List<Avatar>>(avatarSize, avatarSize)
 
-    val preloadModelProvider = object : ListPreloader.PreloadModelProvider<List<String>> {
+    val preloadModelProvider = object : ListPreloader.PreloadModelProvider<List<Avatar>> {
       override fun getPreloadItems(position: Int) = listOfNotNull(
         nickListAdapter[position]?.let { AvatarHelper.avatar(messageSettings, it) }
       )
 
-      override fun getPreloadRequestBuilder(item: List<String>) =
+      override fun getPreloadRequestBuilder(item: List<Avatar>) =
         GlideApp.with(this@NickListFragment).loadWithFallbacks(item)?.override(avatarSize)
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
index cf1e4bcc876b94af73537313bd04d7f7f582220b..7ec0c6ff99db5f2b66917e5d6691269d0d7cefc3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
@@ -276,6 +276,12 @@ class AboutFragment : DaggerFragment() {
         ),
         url = "https://github.com/ReactiveX/RxJava"
       ),
+      Library(
+        name = "Retrofit",
+        version = "2.4.0",
+        license = apache2,
+        url = "https://square.github.io/retrofit/"
+      ),
       Library(
         name = "RxJava",
         version = "2.1.9",
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt b/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
index 3486e5b637c1154dcd2261bc95953fe65a2b3431..3cab7a984dc57eee6083e0edfb3c64c215741209 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/Patterns.kt
@@ -67,7 +67,9 @@ object Patterns {
   @Language("RegExp")
   const val LOCAL_HOST_NAME = """(?:$IRI_LABEL\.)*$IRI_LABEL"""
 
-  val DOMAIN_NAME = Regex("""(?:$LOCAL_HOST_NAME|$HOST_NAME|$IP_ADDRESS_STRING)""")
+  @Language("RegExp")
+  const val DOMAIN_NAME_STR = """(?:$LOCAL_HOST_NAME|$HOST_NAME|$IP_ADDRESS_STRING)"""
+  val DOMAIN_NAME = Regex(DOMAIN_NAME_STR)
   /**
    * Regular expression for valid email characters. Does not include some of the valid characters
    * defined in RFC5321: #&~!^`{}/=$*?|
@@ -94,11 +96,21 @@ object Patterns {
    * Regular expression pattern to match email addresses. It excludes double quoted local parts
    * and the special characters #&~!^`{}/=$*?| that are included in RFC5321.
    */
-  val AUTOLINK_EMAIL_ADDRESS = Regex("""($WORD_BOUNDARY(?:$EMAIL_ADDRESS_LOCAL_PART@$EMAIL_ADDRESS_DOMAIN)$WORD_BOUNDARY)""")
+  @Language("RegExp")
+  val AUTOLINK_EMAIL_ADDRESS_STR = """($WORD_BOUNDARY(?:$EMAIL_ADDRESS_LOCAL_PART@$EMAIL_ADDRESS_DOMAIN)$WORD_BOUNDARY)"""
+  val AUTOLINK_EMAIL_ADDRESS = Regex(AUTOLINK_EMAIL_ADDRESS_STR)
 
   /**
    * Regular expression pattern to match IRCCloud user idents.
    */
+  @Language("RegExp")
   const val IRCCLOUD_IDENT_STR = """(?:~?)[us]id(\d+)"""
   val IRCCLOUD_IDENT = Regex(IRCCLOUD_IDENT_STR)
+
+  /**
+   * Regular expression pattern to match Matrix user realnames.
+   */
+  @Language("RegExp")
+  const val MATRIX_REALNAME_STR = """^@[^:]+:$DOMAIN_NAME_STR$"""
+  val MATRIX_REALNAME = Regex(MATRIX_REALNAME_STR)
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/AvatarHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt
similarity index 65%
rename from app/src/main/java/de/kuschku/quasseldroid/util/AvatarHelper.kt
rename to app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt
index ed4f4e536b55fb903d657feecba299d93f3d7ec1..79edf1367ea1ddf55010bee7a8f7d46b8a990cf4 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/AvatarHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/AvatarHelper.kt
@@ -17,48 +17,64 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.kuschku.quasseldroid.util
+package de.kuschku.quasseldroid.util.avatars
 
 import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.libquassel.util.irc.IrcCaseMappers
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
 import de.kuschku.quasseldroid.settings.MessageSettings
+import de.kuschku.quasseldroid.util.Patterns
 import de.kuschku.quasseldroid.util.backport.codec.Hex
 import de.kuschku.quasseldroid.util.helper.letIf
 import de.kuschku.quasseldroid.util.helper.notBlank
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
 import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem
 import org.apache.commons.codec.digest.DigestUtils
 
 object AvatarHelper {
   fun avatar(settings: MessageSettings, message: QuasselDatabase.NotificationData,
              size: Int? = null) = listOfNotNull(
-    message.avatarUrl.notBlank()?.let { listOf(it) },
+    message.avatarUrl.notBlank()?.let { listOf(Avatar.NativeAvatar(it)) },
     settings.showIRCCloudAvatars.letIf {
-      ircCloudFallback(HostmaskHelper.user(message.sender), size)
+      ircCloudFallback(HostmaskHelper.user(message.sender),
+                       size)
     },
     settings.showGravatarAvatars.letIf {
       gravatarFallback(message.realName, size)
+    },
+    settings.showMatrixAvatars.letIf {
+      matrixFallback(message.realName, size)
     }
   ).flatten()
 
   fun avatar(settings: MessageSettings, message: QuasselDatabase.MessageData,
              size: Int? = null) = listOfNotNull(
-    message.avatarUrl.notBlank()?.let { listOf(it) },
+    message.avatarUrl.notBlank()?.let { listOf(Avatar.NativeAvatar(it)) },
     settings.showIRCCloudAvatars.letIf {
-      ircCloudFallback(HostmaskHelper.user(message.sender), size)
+      ircCloudFallback(HostmaskHelper.user(message.sender),
+                       size)
     },
     settings.showGravatarAvatars.letIf {
       gravatarFallback(message.realName, size)
+    },
+    settings.showMatrixAvatars.letIf {
+      matrixFallback(message.realName, size)
     }
   ).flatten()
 
   fun avatar(settings: MessageSettings, user: IrcUserItem, size: Int? = null) = listOfNotNull(
     settings.showIRCCloudAvatars.letIf {
-      ircCloudFallback(HostmaskHelper.user(user.hostmask), size)
+      ircCloudFallback(HostmaskHelper.user(user.hostmask),
+                       size)
     },
     settings.showGravatarAvatars.letIf {
-      gravatarFallback(user.realname.toString(), size)
+      gravatarFallback(user.realname.toString(),
+                       size)
+    },
+    settings.showMatrixAvatars.letIf {
+      matrixFallback(user.realname.toString(),
+                     size)
     }
   ).flatten()
 
@@ -68,10 +84,32 @@ object AvatarHelper {
     },
     settings.showGravatarAvatars.letIf {
       gravatarFallback(user.realName(), size)
+    },
+    settings.showMatrixAvatars.letIf {
+      matrixFallback(user.realName(), size)
     }
   ).flatten()
 
-  private fun gravatarFallback(realname: String, size: Int?): List<String> {
+  private fun ircCloudFallback(ident: String, size: Int?): List<Avatar> {
+    val userId = Patterns.IRCCLOUD_IDENT.matchEntire(ident)?.groupValues?.lastOrNull()
+                 ?: return emptyList()
+
+    if (size != null) {
+      return listOf(
+        Avatar.IRCCloudAvatar(
+          "https://static.irccloud-cdn.com/avatar-redirect/s${truncateSize(size)}/$userId"
+        )
+      )
+    }
+
+    return listOf(
+      Avatar.IRCCloudAvatar(
+        "https://static.irccloud-cdn.com/avatar-redirect/$userId"
+      )
+    )
+  }
+
+  private fun gravatarFallback(realname: String, size: Int?): List<Avatar> {
     return Patterns.AUTOLINK_EMAIL_ADDRESS
       .findAll(realname)
       .mapNotNull {
@@ -83,18 +121,17 @@ object AvatarHelper {
         } else {
           "https://www.gravatar.com/avatar/$hash?d=404&s=${truncateSize(size)}"
         }
-      }.toList()
+      }.map { Avatar.GravatarAvatar(it) }.toList()
   }
 
-  private fun ircCloudFallback(ident: String, size: Int?): List<String> {
-    val userId = Patterns.IRCCLOUD_IDENT.matchEntire(ident)?.groupValues?.lastOrNull()
-                 ?: return emptyList()
-
-    if (size != null) {
-      return listOf("https://static.irccloud-cdn.com/avatar-redirect/s${truncateSize(size)}/$userId")
+  private fun matrixFallback(realname: String, size: Int?): List<Avatar> {
+    return if (Patterns.MATRIX_REALNAME.matches(realname)) {
+      listOf(
+        Avatar.MatrixAvatar(realname, size?.let(this::truncateSize))
+      )
+    } else {
+      emptyList()
     }
-
-    return listOf("https://static.irccloud-cdn.com/avatar-redirect/$userId")
   }
 
   private fun truncateSize(originalSize: Int) = if (originalSize > 72) 512 else 72
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixApi.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixApi.kt
new file mode 100644
index 0000000000000000000000000000000000000000..842a5e0bfb6a408582c070459743bb83e7de8746
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixApi.kt
@@ -0,0 +1,46 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.util.avatars
+
+import okhttp3.ResponseBody
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface MatrixApi {
+  @GET("/_matrix/client/r0/profile/{name}/avatar_url")
+  fun avatarUrl(@Path("name") name: String): Call<MatrixAvatarResponse>
+
+  @GET("/_matrix/media/r0/thumbnail/{server}/{id}/?width=32&height=32&method=crop")
+  fun avatarThumbnail(
+    @Path("server") server: String,
+    @Path("id") id: String,
+    @Query("width") width: Int = 512,
+    @Query("height") height: Int = 512,
+    @Query("method") method: String = "scale"
+  ): Call<ResponseBody>
+
+  @GET("/_matrix/media/r0/download/{server}/{id}")
+  fun avatarImage(
+    @Path("server") server: String,
+    @Path("id") id: String
+  ): Call<ResponseBody>
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b87619ed4614a3bfa2885ff7edbf076b253d12b8
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarInfo.kt
@@ -0,0 +1,25 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.util.avatars
+
+data class MatrixAvatarInfo(
+  val avatarUrl: String,
+  val size: Int?
+)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarResponse.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dacaf521c9e21fa910c46e2ee8e5a9a600d3a0b2
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixAvatarResponse.kt
@@ -0,0 +1,28 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.util.avatars
+
+import com.google.gson.annotations.SerializedName
+
+data class MatrixAvatarResponse(
+  @SerializedName("avatar_url")
+  val avatarUrl: String?
+)
+
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixDataFetcher.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixDataFetcher.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0835d624cfddc57463a8670a387f6b0f8276bb67
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixDataFetcher.kt
@@ -0,0 +1,79 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.util.avatars
+
+import android.net.Uri
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.data.DataFetcher
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
+import okhttp3.Call
+import java.io.InputStream
+
+class MatrixDataFetcher(
+  private val model: Avatar.MatrixAvatar,
+  private val api: MatrixApi
+) :
+  DataFetcher<InputStream> {
+  private var call: Call? = null
+
+  override fun getDataClass(): Class<InputStream> {
+    return InputStream::class.java
+  }
+
+  override fun cleanup() {
+  }
+
+  override fun getDataSource(): DataSource {
+    return DataSource.REMOTE
+  }
+
+  override fun cancel() {
+    call?.cancel()
+  }
+
+  private fun loadAvatarInfo(model: Avatar.MatrixAvatar) =
+    api.avatarUrl(model.userId).execute().body()?.let {
+      it.avatarUrl?.let {
+        MatrixAvatarInfo(it, model.size)
+      }
+    }
+
+  private fun inputStreamFromAvatarInfo(info: MatrixAvatarInfo): InputStream? {
+    val url = Uri.parse(info.avatarUrl)
+    return if (info.size != null && info.size < 512) {
+      api.avatarThumbnail(server = url.host,
+                          id = url.pathSegments.first(),
+                          width = info.size,
+                          height = info.size,
+                          method = if (info.size > 96) "scale" else "crop")
+    } else {
+      api.avatarImage(server = url.host, id = url.pathSegments.first())
+    }.execute().body()?.byteStream()
+  }
+
+  override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
+    loadAvatarInfo(model)?.let {
+      inputStreamFromAvatarInfo(it)
+    }?.let {
+      callback.onDataReady(it)
+    } ?: callback.onLoadFailed(IllegalStateException("Unknown Error!"))
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixModelLoader.kt b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixModelLoader.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8e05c6436c888fc99514040d2af6a664d9e2686b
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/avatars/MatrixModelLoader.kt
@@ -0,0 +1,37 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.util.avatars
+
+import com.bumptech.glide.load.Options
+import com.bumptech.glide.load.model.ModelLoader
+import com.bumptech.glide.signature.ObjectKey
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
+import java.io.InputStream
+
+class MatrixModelLoader(val api: MatrixApi) : ModelLoader<Avatar.MatrixAvatar, InputStream> {
+  override fun buildLoadData(model: Avatar.MatrixAvatar, width: Int, height: Int,
+                             options: Options): ModelLoader.LoadData<InputStream>? {
+    return ModelLoader.LoadData(ObjectKey(model), MatrixDataFetcher(model, api))
+  }
+
+  override fun handles(model: Avatar.MatrixAvatar): Boolean {
+    return true
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/helper/GlideHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/helper/GlideHelper.kt
index 78278bcbbfd61fcb31fbd0ecccccc55506288f9c..11f013106b3f6aa7f31227f660218786f549c283 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/helper/GlideHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/helper/GlideHelper.kt
@@ -25,10 +25,16 @@ import com.bumptech.glide.RequestBuilder
 import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.GlideRequest
 import de.kuschku.quasseldroid.GlideRequests
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
 
-fun GlideRequests.loadWithFallbacks(urls: List<String>): GlideRequest<Drawable>? {
-  fun fold(url: String, fallback: RequestBuilder<Drawable>?): GlideRequest<Drawable> {
-    return this.load(url).let {
+fun GlideRequests.loadWithFallbacks(urls: List<Avatar>): GlideRequest<Drawable>? {
+  fun fold(url: Avatar, fallback: RequestBuilder<Drawable>?): GlideRequest<Drawable> {
+    return when (url) {
+      is Avatar.NativeAvatar   -> this.load(url.url)
+      is Avatar.GravatarAvatar -> this.load(url.url)
+      is Avatar.IRCCloudAvatar -> this.load(url.url)
+      is Avatar.MatrixAvatar   -> this.load(url)
+    }.let {
       if (fallback != null) it.error(fallback) else it
     }
   }
@@ -36,7 +42,7 @@ fun GlideRequests.loadWithFallbacks(urls: List<String>): GlideRequest<Drawable>?
   return urls.foldRight(null, ::fold)
 }
 
-fun ImageView.loadAvatars(urls: List<String>, fallback: Drawable? = null, crop: Boolean = true) {
+fun ImageView.loadAvatars(urls: List<Avatar>, fallback: Drawable? = null, crop: Boolean = true) {
   if (urls.isNotEmpty()) {
     GlideApp.with(this)
       .loadWithFallbacks(urls)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
index bcb0b75bfa0f47070863f07c93e4b95c747949ea..7fdbf5fc44680cd91218f3037aede2eb50cd5387 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/BackendServiceConnection.kt
@@ -35,12 +35,15 @@ class BackendServiceConnection : ServiceConnection {
 
   var context: Context? = null
 
-  var bound: Boolean = false
+  private var bound: Boolean = false
+
+  private var binding: Boolean = false
 
   override fun onServiceDisconnected(component: ComponentName?) {
     when (component) {
       ComponentName(context, QuasselService::class.java) -> {
         bound = false
+        binding = false
         backend.onNext(Optional.empty())
       }
     }
@@ -51,6 +54,7 @@ class BackendServiceConnection : ServiceConnection {
       ComponentName(context, QuasselService::class.java) ->
         if (binder is QuasselBinder) {
           bound = true
+          binding = false
           backend.onNext(Optional.of(binder.backend))
         }
     }
@@ -60,15 +64,23 @@ class BackendServiceConnection : ServiceConnection {
     context?.startService(intent)
   }
 
+  @Synchronized
   fun bind(intent: Intent = QuasselService.intent(context!!), flags: Int = 0) {
-    if (!this.bound) context?.bindService(intent, this, flags)
+    if (!this.bound && !this.binding) {
+      this.binding = true
+      context?.bindService(intent, this, flags)
+    }
   }
 
   fun stop(intent: Intent = QuasselService.intent(context!!)) {
     context?.stopService(intent)
   }
 
+  @Synchronized
   fun unbind() {
-    if (this.bound) context?.unbindService(this)
+    if (this.bound && !this.binding) {
+      this.binding = true
+      context?.unbindService(this)
+    }
   }
 }
diff --git a/app/src/main/res/values-de/strings_preferences.xml b/app/src/main/res/values-de/strings_preferences.xml
index 3295059cb8fc54c6f16f084c120f551cdb7b99e3..184f098572c3a127415fff48eeaca50e8e8cdc6c 100644
--- a/app/src/main/res/values-de/strings_preferences.xml
+++ b/app/src/main/res/values-de/strings_preferences.xml
@@ -93,6 +93,9 @@
   <string name="preference_show_gravatar_avatars_title">Gravatar Avatare anzeigen</string>
   <string name="preference_show_gravatar_avatars_summary">Zeigt für Nutzer ohne Avatare den Gravatar-Avatar an, falls verfügbar</string>
 
+  <string name="preference_show_matrix_avatars_title">Matrix Avatare anzeigen</string>
+  <string name="preference_show_matrix_avatars_summary">Zeigt für Nutzer ohne Avatare den Matrix-Avatar an, falls verfügbar</string>
+
   <string name="preference_time_at_end_title">Rechts-Ausgerichtete Zeit</string>
   <string name="preference_time_at_end_summary">Zeigt die Zeit rechts in Nachrichten an</string>
 
diff --git a/app/src/main/res/values/strings_preferences.xml b/app/src/main/res/values/strings_preferences.xml
index 9ba628c8706d1ebc07ed247f91badcbef8434a5a..c638b1a7d5886784cd14346402573c10830ddd35 100644
--- a/app/src/main/res/values/strings_preferences.xml
+++ b/app/src/main/res/values/strings_preferences.xml
@@ -177,6 +177,10 @@
   <string name="preference_show_gravatar_avatars_title">Show Gravatar Avatars</string>
   <string name="preference_show_gravatar_avatars_summary">Shows for users without avatar their Gravatar fallback, if available</string>
 
+  <string name="preference_show_matrix_avatars_key" translatable="false">show_matrix_avatars</string>
+  <string name="preference_show_matrix_avatars_title">Show Matrix Avatars</string>
+  <string name="preference_show_matrix_avatars_summary">Shows for users without avatar their Matrix fallback, if available</string>
+
   <string name="preference_time_at_end_key" translatable="false">time_at_end</string>
   <string name="preference_time_at_end_title">Right-Aligned Timestamps</string>
   <string name="preference_time_at_end_summary">Aligns timestamps at the end of each message</string>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index a270c1f55307028ed70fb198cc28c2b4e08c9254..ba8aa707798d29a8e37858aaf5a03e53f8d0881d 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -128,6 +128,12 @@
       android:key="@string/preference_show_gravatar_avatars_key"
       android:title="@string/preference_show_gravatar_avatars_title" />
 
+    <SwitchPreference
+      android:defaultValue="false"
+      android:dependency="@string/preference_show_avatars_key"
+      android:key="@string/preference_show_matrix_avatars_key"
+      android:title="@string/preference_show_matrix_avatars_title" />
+
     <SwitchPreference
       android:defaultValue="true"
       android:key="@string/preference_nicks_on_new_line_key"
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 6fb9fcc1b14131012538dc1411146840122d424e..cdcf7b3f9936db812e3a7e671c077532bdf57563 100644
--- a/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt
+++ b/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt
@@ -23,6 +23,7 @@ import de.kuschku.libquassel.protocol.Message_Flag
 import de.kuschku.libquassel.protocol.Message_Type
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
 import de.kuschku.quasseldroid.settings.MessageSettings
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import org.junit.Test
 import org.threeten.bp.Instant
 
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
index 6dc1a75b25bd038f4353512535fa65ae637d9a4b..afa1e13f0d35ee1e4fe879ba858fc4894bcb0c54 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
@@ -41,7 +41,7 @@ sealed class AutoCompleteItem(open val name: String) : Comparable<AutoCompleteIt
     val realname: CharSequence,
     val away: Boolean,
     val networkCasemapping: String?,
-    val avatarUrls: List<String> = emptyList(),
+    val avatarUrls: List<Avatar> = emptyList(),
     val fallbackDrawable: Drawable? = null,
     val displayNick: CharSequence? = null
   ) : AutoCompleteItem(nick)
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8c83036467212117567fd718f418f5825186462d
--- /dev/null
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/Avatar.kt
@@ -0,0 +1,27 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 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.viewmodel.data
+
+sealed class Avatar {
+  data class NativeAvatar(val url: String) : Avatar()
+  data class IRCCloudAvatar(val url: String) : Avatar()
+  data class GravatarAvatar(val url: String) : Avatar()
+  data class MatrixAvatar(val userId: String, val size: Int?) : Avatar()
+}
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt
index 6eda804f1afd8106f707405d901d541f34360ea0..2a9088b19f339ec62ab24751dfdf417116c5dcb5 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/FormattedMessage.kt
@@ -30,7 +30,7 @@ class FormattedMessage(
   val combined: CharSequence,
   val fallbackDrawable: Drawable? = null,
   val realName: CharSequence? = null,
-  val avatarUrls: List<String> = emptyList(),
+  val avatarUrls: List<Avatar> = emptyList(),
   val isSelected: Boolean,
   val isExpanded: Boolean,
   val isMarkerLine: Boolean
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
index f7c4474118aadfefe14cbb3cd97d987dd720bc22..121ca2bed059d5e4924872188ae6407beb28ee75 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
@@ -29,7 +29,7 @@ data class IrcUserItem(
   val hostmask: String,
   val away: Boolean,
   val networkCasemapping: String?,
-  val avatarUrls: List<String> = emptyList(),
+  val avatarUrls: List<Avatar> = emptyList(),
   val fallbackDrawable: Drawable? = null,
   val displayNick: CharSequence? = null
 )