From 4095e25c3e60bf7c9118f25c1fa9ec0a0625a824 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Wed, 6 Jun 2018 22:57:12 +0200
Subject: [PATCH] Show avatars in buffer list

---
 .../ui/chat/buffers/BufferListAdapter.kt      | 124 +++++++++---------
 .../chat/buffers/BufferViewConfigFragment.kt  |  48 ++++++-
 .../ui/chat/nicks/NickListFragment.kt         |  12 +-
 .../main/res/drawable/ic_status_channel.xml   |  10 +-
 .../drawable/ic_status_channel_offline.xml    |  10 +-
 .../main/res/drawable/ic_status_offline.xml   |   6 +-
 app/src/main/res/layout/widget_buffer.xml     |  39 +++---
 .../main/res/layout/widget_buffer_away.xml    |  88 +++++++++++++
 app/src/main/res/values/dimens.xml            |   1 +
 .../viewmodel/QuasselViewModel.kt             |   3 +-
 .../viewmodel/data/BufferProps.kt             |   7 +-
 .../viewmodel/data/BufferStatus.kt            |  13 +-
 12 files changed, 250 insertions(+), 111 deletions(-)
 create mode 100644 app/src/main/res/layout/widget_buffer_away.xml

diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
index 749a8669c..4a329ce63 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt
@@ -36,10 +36,8 @@ import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.quasseldroid.R
-import de.kuschku.quasseldroid.util.helper.getVectorDrawableCompat
-import de.kuschku.quasseldroid.util.helper.styledAttributes
-import de.kuschku.quasseldroid.util.helper.tint
-import de.kuschku.quasseldroid.util.helper.visibleIf
+import de.kuschku.quasseldroid.settings.MessageSettings
+import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.lists.ListAdapter
 import de.kuschku.quasseldroid.viewmodel.data.BufferListItem
 import de.kuschku.quasseldroid.viewmodel.data.BufferProps
@@ -48,6 +46,7 @@ import de.kuschku.quasseldroid.viewmodel.data.BufferStatus
 import io.reactivex.subjects.BehaviorSubject
 
 class BufferListAdapter(
+  private val messageSettings: MessageSettings,
   private val selectedBuffer: BehaviorSubject<BufferId>,
   private val expandedNetworks: BehaviorSubject<Map<NetworkId, Boolean>>
 ) : ListAdapter<BufferListItem, BufferListAdapter.BufferViewHolder>(
@@ -94,47 +93,56 @@ class BufferListAdapter(
     selectedBuffer.onNext(Int.MAX_VALUE)
   }
 
-  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
-    BufferInfo.Type.ChannelBuffer.toInt() -> BufferViewHolder.ChannelBuffer(
-      LayoutInflater.from(parent.context).inflate(
-        R.layout.widget_buffer, parent, false
-      ),
-      clickListener = clickListener,
-      longClickListener = longClickListener
-    )
-    BufferInfo.Type.QueryBuffer.toInt()   -> BufferViewHolder.QueryBuffer(
-      LayoutInflater.from(parent.context).inflate(
-        R.layout.widget_buffer, parent, false
-      ),
-      clickListener = clickListener,
-      longClickListener = longClickListener
-    )
-    BufferInfo.Type.GroupBuffer.toInt()   -> BufferViewHolder.GroupBuffer(
-      LayoutInflater.from(parent.context).inflate(
-        R.layout.widget_buffer, parent, false
-      ),
-      clickListener = clickListener,
-      longClickListener = longClickListener
-    )
-    BufferInfo.Type.StatusBuffer.toInt()  -> BufferViewHolder.StatusBuffer(
-      LayoutInflater.from(parent.context).inflate(
-        R.layout.widget_network, parent, false
-      ),
-      clickListener = clickListener,
-      longClickListener = longClickListener,
-      expansionListener = ::expandListener
-    )
-    else                                  -> throw IllegalArgumentException(
-      "No such viewType: $viewType"
-    )
+  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BufferViewHolder {
+    val bufferType = viewType and 0xFFFF
+    val bufferStatus = BufferStatus.of(((viewType ushr 16) and 0xFFFF).toShort())
+    return when (bufferType) {
+      BufferInfo.Type.ChannelBuffer.toInt() -> BufferViewHolder.ChannelBuffer(
+        LayoutInflater.from(parent.context).inflate(
+          R.layout.widget_buffer, parent, false
+        ),
+        clickListener = clickListener,
+        longClickListener = longClickListener
+      )
+      BufferInfo.Type.QueryBuffer.toInt()   -> BufferViewHolder.QueryBuffer(
+        LayoutInflater.from(parent.context).inflate(
+          if (bufferStatus == BufferStatus.AWAY) R.layout.widget_buffer_away
+          else R.layout.widget_buffer
+          , parent, false
+        ),
+        clickListener = clickListener,
+        longClickListener = longClickListener
+      )
+      BufferInfo.Type.GroupBuffer.toInt()   -> BufferViewHolder.GroupBuffer(
+        LayoutInflater.from(parent.context).inflate(
+          R.layout.widget_buffer, parent, false
+        ),
+        clickListener = clickListener,
+        longClickListener = longClickListener
+      )
+      BufferInfo.Type.StatusBuffer.toInt()  -> BufferViewHolder.StatusBuffer(
+        LayoutInflater.from(parent.context).inflate(
+          R.layout.widget_network, parent, false
+        ),
+        clickListener = clickListener,
+        longClickListener = longClickListener,
+        expansionListener = ::expandListener
+      )
+      else                                  -> throw IllegalArgumentException(
+        "No such viewType: ${viewType.toString(16)}"
+      )
+    }
   }
 
   override fun onBindViewHolder(holder: BufferViewHolder, position: Int) =
-    holder.bind(getItem(position).props, getItem(position).state)
+    holder.bind(getItem(position).props, getItem(position).state, messageSettings)
+
+  override fun getItemViewType(position: Int) = getItem(position).let {
+    (it.props.bufferStatus.ordinal shl 16) + (it.props.info.type.toInt() and 0xFFFF)
+  }
 
-  override fun getItemViewType(position: Int) = getItem(position).props.info.type.toInt()
   abstract class BufferViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
-    abstract fun bind(props: BufferProps, state: BufferState)
+    abstract fun bind(props: BufferProps, state: BufferState, messageSettings: MessageSettings)
 
     class StatusBuffer(
       itemView: View,
@@ -193,7 +201,7 @@ class BufferListAdapter(
         }
       }
 
-      override fun bind(props: BufferProps, state: BufferState) {
+      override fun bind(props: BufferProps, state: BufferState, messageSettings: MessageSettings) {
         name.text = props.network.networkName
         bufferId = props.info.bufferId
         networkId = props.info.networkId
@@ -279,7 +287,7 @@ class BufferListAdapter(
         }
       }
 
-      override fun bind(props: BufferProps, state: BufferState) {
+      override fun bind(props: BufferProps, state: BufferState, messageSettings: MessageSettings) {
         bufferId = props.info.bufferId
 
         name.text = props.info.bufferName
@@ -367,7 +375,7 @@ class BufferListAdapter(
         }
       }
 
-      override fun bind(props: BufferProps, state: BufferState) {
+      override fun bind(props: BufferProps, state: BufferState, messageSettings: MessageSettings) {
         bufferId = props.info.bufferId
 
         name.text = props.info.bufferName
@@ -411,8 +419,6 @@ class BufferListAdapter(
 
       var bufferId: BufferId? = null
 
-      private val online: Drawable?
-      private val away: Drawable?
       private val offline: Drawable?
 
       private var none: Int = 0
@@ -438,27 +444,23 @@ class BufferListAdapter(
           }
         }
 
-        online = itemView.context.getVectorDrawableCompat(R.drawable.ic_status)?.mutate()
-        away = itemView.context.getVectorDrawableCompat(R.drawable.ic_status)?.mutate()
         offline = itemView.context.getVectorDrawableCompat(R.drawable.ic_status_offline)?.mutate()
 
         itemView.context.theme.styledAttributes(
-          R.attr.colorAccent, R.attr.colorAway,
+          R.attr.colorAway,
           R.attr.colorTextPrimary, R.attr.colorTintActivity, R.attr.colorTintMessage,
           R.attr.colorTintHighlight
         ) {
-          online?.tint(getColor(0, 0))
-          away?.tint(getColor(1, 0))
-          offline?.tint(getColor(1, 0))
+          offline?.tint(getColor(0, 0))
 
-          none = getColor(2, 0)
-          activity = getColor(3, 0)
-          message = getColor(4, 0)
-          highlight = getColor(5, 0)
+          none = getColor(1, 0)
+          activity = getColor(2, 0)
+          message = getColor(3, 0)
+          highlight = getColor(4, 0)
         }
       }
 
-      override fun bind(props: BufferProps, state: BufferState) {
+      override fun bind(props: BufferProps, state: BufferState, messageSettings: MessageSettings) {
         bufferId = props.info.bufferId
 
         name.text = props.info.bufferName
@@ -477,13 +479,9 @@ class BufferListAdapter(
 
         description.visibleIf(props.description.isNotBlank())
 
-        status.setImageDrawable(
-          when (props.bufferStatus) {
-            BufferStatus.ONLINE -> online
-            BufferStatus.AWAY   -> away
-            else                -> offline
-          }
-        )
+        status.loadAvatars(props.avatarUrls,
+                           props.fallbackDrawable ?: offline,
+                           crop = !messageSettings.squareAvatars)
       }
     }
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
index da67e284a..75fc022f1 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -38,16 +38,21 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.flag.minus
 import de.kuschku.libquassel.util.helpers.value
+import de.kuschku.libquassel.util.irc.SenderColorUtil
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.coresettings.network.NetworkEditActivity
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.combineLatest
+import de.kuschku.quasseldroid.util.helper.styledAttributes
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.helper.zip
 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.EditorViewModel.Companion.IGNORED_CHARS
 import de.kuschku.quasseldroid.viewmodel.data.BufferHiddenState
 import de.kuschku.quasseldroid.viewmodel.data.BufferListItem
 import de.kuschku.quasseldroid.viewmodel.data.BufferState
@@ -245,9 +250,28 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
     chatListSpinner.adapter = adapter
 
     listAdapter = BufferListAdapter(
+      messageSettings,
       viewModel.selectedBufferId,
       viewModel.expandedNetworks
     )
+
+    val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size_buffer)
+
+    val senderColors = requireContext().theme.styledAttributes(
+      R.attr.senderColor0, R.attr.senderColor1, R.attr.senderColor2, R.attr.senderColor3,
+      R.attr.senderColor4, R.attr.senderColor5, R.attr.senderColor6, R.attr.senderColor7,
+      R.attr.senderColor8, R.attr.senderColor9, R.attr.senderColorA, R.attr.senderColorB,
+      R.attr.senderColorC, R.attr.senderColorD, R.attr.senderColorE, R.attr.senderColorF
+    ) {
+      IntArray(length()) {
+        getColor(it, 0)
+      }
+    }
+
+    val selfColor = requireContext().theme.styledAttributes(R.attr.colorForegroundSecondary) {
+      getColor(0, 0)
+    }
+
     combineLatest(viewModel.bufferList, viewModel.expandedNetworks, viewModel.selectedBuffer)
       .toLiveData().zip(database.filtered().listen(accountId))
       .observe(this, Observer { it ->
@@ -279,7 +303,29 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
                       activity.isNotEmpty()                 -> Buffer_Activity.OtherActivity
                       else                                  -> Buffer_Activity.NoActivity
                     }
-                  )
+                  ),
+                  fallbackDrawable = props.ircUser?.let {
+                    val nickName = it.nick()
+                    val senderColorIndex = SenderColorUtil.senderColor(nickName)
+                    val rawInitial = nickName.trimStart(*IGNORED_CHARS).firstOrNull()
+                                     ?: nickName.firstOrNull()
+                    val initial = rawInitial?.toUpperCase().toString()
+                    val senderColor = when (messageSettings.colorizeNicknames) {
+                      MessageSettings.ColorizeNicknamesMode.ALL          -> senderColors[senderColorIndex]
+                      MessageSettings.ColorizeNicknamesMode.ALL_BUT_MINE ->
+                        if (props.ircUser?.network()?.isMyNick(nickName) == true) selfColor
+                        else senderColors[senderColorIndex]
+                      MessageSettings.ColorizeNicknamesMode.NONE         -> selfColor
+                    }
+
+                    TextDrawable.builder().let {
+                      if (messageSettings.squareAvatars) it.buildRect(initial, senderColor)
+                      else it.buildRound(initial, senderColor)
+                    }
+                  },
+                  avatarUrls = props.ircUser?.let {
+                    AvatarHelper.avatar(messageSettings, it, avatarSize)
+                  } ?: emptyList()
                 ),
                 BufferState(
                   networkExpanded = expandedNetworks[props.network.networkId]
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 936fbe74f..80f6d2ce1 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
@@ -104,17 +104,7 @@ class NickListFragment : ServiceBoundFragment() {
         it?.asSequence()?.map {
           val nickName = it.nick
           val senderColorIndex = SenderColorUtil.senderColor(nickName)
-          val rawInitial = nickName.trimStart('-',
-                                              '_',
-                                              '[',
-                                              ']',
-                                              '{',
-                                              '}',
-                                              '|',
-                                              '`',
-                                              '^',
-                                              '.',
-                                              '\\')
+          val rawInitial = nickName.trimStart(*IGNORED_CHARS)
                              .firstOrNull() ?: nickName.firstOrNull()
           val initial = rawInitial?.toUpperCase().toString()
           val senderColor = when (messageSettings.colorizeNicknames) {
diff --git a/app/src/main/res/drawable/ic_status_channel.xml b/app/src/main/res/drawable/ic_status_channel.xml
index 64196ef0b..954f227f3 100644
--- a/app/src/main/res/drawable/ic_status_channel.xml
+++ b/app/src/main/res/drawable/ic_status_channel.xml
@@ -20,15 +20,15 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
-  android:viewportHeight="24"
-  android:viewportWidth="24">
+  android:viewportHeight="20"
+  android:viewportWidth="20">
   <path
     android:fillColor="#000"
-    android:pathData="M2,12a10,10 0 1,0 20,0 10,10 0 1,0-20,0
-    M9.7,9.7l0.295,-2.13a0.625,0.625 0 0 1 1.238,0.17l-0.27,1.953 2.779,-0.015 0.293,-2.109a
+    android:pathData="M0,10a10,10 0 1,0 20,0 10,10 0 1,0-20,0
+    M7.7,7.7l0.295,-2.13a0.625,0.625 0 0 1 1.238,0.17l-0.27,1.953 2.779,-0.015 0.293,-2.109a
     0.625,0.625 0 0 1 1.239,0.171l-0.268,1.932 1.609,0a0.625,0.625 0 1 1 0,1.25l-1.789,0-0.331,2.388
     1.712,0a0.625,0.625 0 0 1 0,1.249l-1.892,0.01-0.305,2.196a0.625,0.625 0 0 1-1.238,-0.172l0.28,
     -2.018-2.78,0.015-0.302,2.175a0.625,0.625 0 0 1-1.238,-0.172l0.277,-1.997-1.418,0a0.625,0.625 0
     0 1 0,-1.249l1.598,0 0.504,-3.638-0.173,1.25-1.521,0a0.625,0.625 0 0 1 0,-1.25z
-    M9.7,9.7m1.089,1.25-0.331,2.388 2.779,-0.015 0.331,-2.387z" />
+    M7.7,7.7m1.089,1.25-0.331,2.388 2.779,-0.015 0.331,-2.387z" />
 </vector>
diff --git a/app/src/main/res/drawable/ic_status_channel_offline.xml b/app/src/main/res/drawable/ic_status_channel_offline.xml
index 989a62f89..b84f8145c 100644
--- a/app/src/main/res/drawable/ic_status_channel_offline.xml
+++ b/app/src/main/res/drawable/ic_status_channel_offline.xml
@@ -20,16 +20,16 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
-  android:viewportHeight="24"
-  android:viewportWidth="24">
+  android:viewportHeight="20"
+  android:viewportWidth="20">
   <path
     android:fillColor="#000"
-    android:pathData="M2,12a10,10 0 1,0 20,0 10,10 0 1,0-20,0
+    android:pathData="M0,10a10,10 0 1,0 20,0 10,10 0 1,0-20,0
     m1.5,0a8.5,8.5 0 0,1 17,0a8.5,8.5 0 0,1-17,0
-    M9.7,9.7l0.295,-2.13a0.625,0.625 0 0 1 1.238,0.17l-0.27,1.953 2.779,-0.015 0.293,-2.109a
+    M7.7,7.7l0.295,-2.13a0.625,0.625 0 0 1 1.238,0.17l-0.27,1.953 2.779,-0.015 0.293,-2.109a
     0.625,0.625 0 0 1 1.239,0.171l-0.268,1.932 1.609,0a0.625,0.625 0 1 1 0,1.25l-1.789,0-0.331,2.388
     1.712,0a0.625,0.625 0 0 1 0,1.249l-1.892,0.01-0.305,2.196a0.625,0.625 0 0 1-1.238,-0.172l0.28,
     -2.018-2.78,0.015-0.302,2.175a0.625,0.625 0 0 1-1.238,-0.172l0.277,-1.997-1.418,0a0.625,0.625 0
     0 1 0,-1.249l1.598,0 0.504,-3.638-0.173,1.25-1.521,0a0.625,0.625 0 0 1 0,-1.25z
-    M9.7,9.7m1.089,1.25-0.331,2.388 2.779,-0.015 0.331,-2.387z" />
+    M7.7,7.7m1.089,1.25-0.331,2.388 2.779,-0.015 0.331,-2.387z" />
 </vector>
diff --git a/app/src/main/res/drawable/ic_status_offline.xml b/app/src/main/res/drawable/ic_status_offline.xml
index bf507501b..292697f16 100644
--- a/app/src/main/res/drawable/ic_status_offline.xml
+++ b/app/src/main/res/drawable/ic_status_offline.xml
@@ -20,10 +20,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
-  android:viewportHeight="24"
-  android:viewportWidth="24">
+  android:viewportHeight="20"
+  android:viewportWidth="20">
   <path
     android:fillColor="#000"
-    android:pathData="M2,12a10,10 0 1,0 20,0 10,10 0 1,0-20,0
+    android:pathData="M0,10a10,10 0 1,0 20,0 10,10 0 1,0-20,0
     m1.5,0a8.5,8.5 0 0,1 17,0a8.5,8.5 0 0,1-17,0" />
 </vector>
diff --git a/app/src/main/res/layout/widget_buffer.xml b/app/src/main/res/layout/widget_buffer.xml
index 4ee1a394c..a751b0fa1 100644
--- a/app/src/main/res/layout/widget_buffer.xml
+++ b/app/src/main/res/layout/widget_buffer.xml
@@ -22,48 +22,53 @@
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="?attr/backgroundMenuItem"
-  android:minHeight="48dp"
-  android:paddingBottom="8dp"
+  android:minHeight="?listPreferredItemHeightSmall"
+  android:orientation="horizontal"
+  android:paddingBottom="4dp"
   android:paddingLeft="16dp"
   android:paddingRight="16dp"
-  android:paddingTop="8dp">
+  android:paddingTop="4dp"
+  android:textAppearance="?android:attr/textAppearanceListItemSmall">
 
-  <android.support.v7.widget.AppCompatImageView
+  <ImageView
     android:id="@+id/status"
-    android:layout_width="24dp"
-    android:layout_height="24dp"
-    android:layout_gravity="center"
-    android:layout_marginEnd="32dp"
-    android:layout_marginRight="32dp"
-    tools:src="@drawable/ic_status_channel"
-    tools:tint="?attr/colorAccent" />
+    android:layout_width="@dimen/avatar_size_buffer"
+    android:layout_height="@dimen/avatar_size_buffer"
+    android:layout_gravity="center_vertical"
+    android:layout_marginEnd="16dp"
+    android:layout_marginRight="16dp"
+    android:contentDescription="@string/label_avatar"
+    tools:src="@tools:sample/avatars" />
 
   <LinearLayout
-    android:layout_width="0dip"
+    android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:layout_weight="1"
+    android:layout_gravity="center_vertical"
     android:orientation="vertical">
 
     <TextView
       android:id="@+id/name"
+      style="@style/Widget.RtlConformTextView"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
+      android:ellipsize="marquee"
       android:layout_gravity="center_vertical|start"
       android:fontFamily="sans-serif-medium"
       android:singleLine="true"
       android:textColor="?attr/colorTextPrimary"
       android:textSize="13sp"
-      tools:text="#quasseldroid" />
+      tools:text="@sample/messages.json/data/sender" />
 
     <TextView
       android:id="@+id/description"
+      style="@style/Widget.RtlConformTextView"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      android:layout_gravity="center_vertical|start"
+      android:ellipsize="marquee"
       android:singleLine="true"
       android:textColor="?attr/colorTextSecondary"
       android:textSize="12sp"
-      tools:text="Quasseldroid is an Android client for #quassel ♥ justJanne's much improved version: https://dl.kuschku.de/releases/quasseldroid/ ♥ http://github.com/sandsmark/Quasseldroid ♥ Quasseldroid  on play  https://market.android.com/details?id=com.iskrembilen.quasseldroid ♥ Sign up for beta: https://plus.google.com/communities/104094956084217666662" />
+      tools:text="@sample/messages.json/data/sender"
+      tools:visibility="visible" />
   </LinearLayout>
 </LinearLayout>
diff --git a/app/src/main/res/layout/widget_buffer_away.xml b/app/src/main/res/layout/widget_buffer_away.xml
new file mode 100644
index 000000000..aabbfe9e3
--- /dev/null
+++ b/app/src/main/res/layout/widget_buffer_away.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  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/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:app="http://schemas.android.com/apk/res-auto"
+  xmlns:tools="http://schemas.android.com/tools"
+  android:layout_width="match_parent"
+  android:layout_height="wrap_content"
+  android:background="?attr/backgroundMenuItem"
+  android:minHeight="?listPreferredItemHeightSmall"
+  android:orientation="horizontal"
+  android:paddingBottom="4dp"
+  android:paddingLeft="16dp"
+  android:paddingRight="16dp"
+  android:paddingTop="4dp"
+  android:textAppearance="?android:attr/textAppearanceListItemSmall">
+
+  <ImageView
+    android:id="@+id/status"
+    android:layout_width="@dimen/avatar_size_buffer"
+    android:layout_height="@dimen/avatar_size_buffer"
+    android:layout_gravity="center_vertical"
+    android:layout_marginEnd="16dp"
+    android:layout_marginRight="16dp"
+    android:contentDescription="@string/label_avatar"
+    tools:src="@drawable/ic_status_channel" />
+
+  <LinearLayout
+    android:layout_width="0dip"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_vertical"
+    android:layout_weight="1"
+    android:orientation="vertical">
+
+    <TextView
+      android:id="@+id/name"
+      style="@style/Widget.RtlConformTextView"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_vertical|start"
+      android:ellipsize="marquee"
+      android:fontFamily="sans-serif-medium"
+      android:singleLine="true"
+      android:textColor="?attr/colorTextSecondary"
+      android:textSize="13sp"
+      android:textStyle="italic"
+      tools:text="@sample/messages.json/data/sender" />
+
+    <TextView
+      android:id="@+id/description"
+      style="@style/Widget.RtlConformTextView"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:ellipsize="marquee"
+      android:singleLine="true"
+      android:textColor="?attr/colorTextSecondary"
+      android:textSize="12sp"
+      android:textStyle="italic"
+      tools:text="@sample/messages.json/data/sender"
+      tools:visibility="visible" />
+  </LinearLayout>
+
+  <android.support.v7.widget.AppCompatImageView
+    android:layout_width="24dp"
+    android:layout_height="24dp"
+    android:layout_gravity="center_vertical"
+    android:layout_marginLeft="16dp"
+    android:layout_marginStart="16dp"
+    android:contentDescription="@string/label_user_away"
+    app:srcCompat="@drawable/ic_clock"
+    app:tint="?colorTextSecondary" />
+</LinearLayout>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index c0d6497fa..2b47dc949 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -37,6 +37,7 @@
   <dimen name="colorchooser_circlesize">56dp</dimen>
   <dimen name="avatar_size">35sp</dimen>
   <dimen name="avatar_size_action">20sp</dimen>
+  <dimen name="avatar_size_buffer">32dp</dimen>
 
   <dimen name="notification_avatar_width">64dp</dimen>
   <dimen name="notification_avatar_height">64dp</dimen>
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
index d2681f79d..6fd665815 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt
@@ -379,7 +379,8 @@ class QuasselViewModel : ViewModel() {
                                       description = user?.realName() ?: "",
                                       activity = activity,
                                       highlights = highlights,
-                                      hiddenState = state
+                                      hiddenState = state,
+                                      ircUser = user
                                     )
                                   }
                                 }
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
index 6aa5b90be..daddbb580 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
@@ -19,9 +19,11 @@
 
 package de.kuschku.quasseldroid.viewmodel.data
 
+import android.graphics.drawable.Drawable
 import de.kuschku.libquassel.protocol.Buffer_Activities
 import de.kuschku.libquassel.protocol.Message_Types
 import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 
 data class BufferProps(
@@ -35,5 +37,8 @@ data class BufferProps(
   val bufferActivity: Buffer_Activities = BufferInfo.Activity.of(
     BufferInfo.Activity.NoActivity
   ),
-  val hiddenState: BufferHiddenState
+  val hiddenState: BufferHiddenState,
+  val ircUser: IrcUser? = null,
+  val avatarUrls: List<Avatar> = emptyList(),
+  val fallbackDrawable: Drawable? = null
 )
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferStatus.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferStatus.kt
index 13a1359ac..b98f08939 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferStatus.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferStatus.kt
@@ -19,8 +19,13 @@
 
 package de.kuschku.quasseldroid.viewmodel.data
 
-enum class BufferStatus {
-  ONLINE,
-  AWAY,
-  OFFLINE
+enum class BufferStatus(val value: Short) {
+  ONLINE(0),
+  AWAY(1),
+  OFFLINE(2);
+
+  companion object {
+    private val map = values().associateBy { it.value }
+    fun of(value: Short) = map[value]
+  }
 }
-- 
GitLab