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 23cdf3f20aef44722f40fedb1b660f98f4f51862..2aa901c1b3d54cc5da7cffe12c206dc92ad8fc86 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
@@ -23,9 +23,6 @@ import android.app.Activity
 import android.content.Context
 import android.content.Intent
 import android.content.SharedPreferences
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.drawable.Drawable
 import android.os.Build
 import android.os.Bundle
 import android.system.ErrnoException
@@ -38,9 +35,6 @@ import android.widget.EditText
 import android.widget.Toast
 import androidx.appcompat.app.ActionBarDrawerToggle
 import androidx.appcompat.widget.Toolbar
-import androidx.core.content.pm.ShortcutInfoCompat
-import androidx.core.content.pm.ShortcutManagerCompat
-import androidx.core.graphics.drawable.IconCompat
 import androidx.core.view.GravityCompat
 import androidx.drawerlayout.widget.DrawerLayout
 import androidx.lifecycle.Observer
@@ -50,8 +44,6 @@ import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.afollestad.materialdialogs.MaterialDialog
-import com.bumptech.glide.request.target.SimpleTarget
-import com.bumptech.glide.request.transition.Transition
 import com.google.android.material.bottomsheet.BottomSheetBehavior
 import de.kuschku.libquassel.connection.ConnectionState
 import de.kuschku.libquassel.connection.ProtocolVersionException
@@ -70,8 +62,6 @@ import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.flag.or
 import de.kuschku.libquassel.util.helpers.nullIf
 import de.kuschku.libquassel.util.helpers.value
-import de.kuschku.libquassel.util.irc.SenderColorUtil
-import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.Keys
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.DefaultNetworkServer
@@ -89,8 +79,6 @@ import de.kuschku.quasseldroid.ui.setup.core.CoreSetupActivity
 import de.kuschku.quasseldroid.ui.setup.network.LinkNetwork
 import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupActivity
 import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
-import de.kuschku.quasseldroid.util.ColorContext
-import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.backport.OsConstants
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.IrcPorts
@@ -102,7 +90,6 @@ import de.kuschku.quasseldroid.util.ui.DragInterceptBottomSheetBehavior
 import de.kuschku.quasseldroid.util.ui.MaterialContentLoadingProgressBar
 import de.kuschku.quasseldroid.util.ui.NickCountDrawable
 import de.kuschku.quasseldroid.util.ui.WarningBarView
-import de.kuschku.quasseldroid.viewmodel.EditorViewModel
 import de.kuschku.quasseldroid.viewmodel.data.BufferData
 import io.reactivex.BackpressureStrategy
 import org.threeten.bp.Instant
@@ -818,10 +805,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     menu?.findItem(R.id.action_filter_messages)?.isVisible =
       (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false ||
        bufferData?.info?.type?.hasFlag(Buffer_Type.QueryBuffer) ?: false)
-    menu?.findItem(R.id.action_create_shortcut)?.isVisible =
-      (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false ||
-       bufferData?.info?.type?.hasFlag(Buffer_Type.QueryBuffer) ?: false) &&
-      Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
     menu?.retint(toolbar.context)
     menu?.findItem(R.id.action_nicklist)?.icon = NickCountDrawable(bufferData?.userCount ?: 0,
                                                                    nickCountDrawableSize,
@@ -912,90 +895,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       }
       true
     }
-    R.id.action_create_shortcut -> {
-      bufferData?.also { data ->
-        data.info?.also { info ->
-          val callback: (IconCompat) -> Unit = { icon ->
-            ShortcutManagerCompat.requestPinShortcut(
-              this,
-              ShortcutInfoCompat.Builder(this, "${System.currentTimeMillis()}")
-                .setShortLabel(info.bufferName ?: "")
-                .setIcon(icon)
-                .setIntent(
-                  ChatActivity.intent(
-                    this,
-                    bufferId = info.bufferId,
-                    accountId = accountId
-                  ).setAction(Intent.ACTION_VIEW)
-                )
-                .build(),
-              null
-            )
-          }
-
-          val resultAvailable: (Drawable) -> Unit = { resource ->
-            val bitmap = Bitmap.createBitmap(432, 432, Bitmap.Config.ARGB_8888)
-            val canvas = Canvas(bitmap)
-            resource.setBounds(0, 0, canvas.width, canvas.height)
-            resource.draw(canvas)
-            callback(IconCompat.createWithAdaptiveBitmap(bitmap))
-          }
-
-          val senderColors = 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 colorContext = ColorContext(this, messageSettings)
-
-          if (info.type.hasFlag(Buffer_Type.QueryBuffer)) {
-            val nickName = info.bufferName ?: ""
-            val senderColorIndex = SenderColorUtil.senderColor(nickName)
-            val rawInitial = nickName.trimStart(*EditorViewModel.IGNORED_CHARS).firstOrNull()
-                             ?: nickName.firstOrNull()
-            val initial = rawInitial?.toUpperCase().toString()
-            val senderColor = senderColors[senderColorIndex]
-
-            val fallback = colorContext.prepareTextDrawable()
-              .beginConfig()
-              .scale(0.5f)
-              .endConfig()
-              .buildRect(initial, senderColor)
-
-            val urls = viewModel.networks.value?.get(info.networkId)?.ircUser(info.bufferName)?.let {
-              AvatarHelper.avatar(messageSettings, it, 432)
-            }
-
-            if (urls == null || urls.isEmpty()) {
-              resultAvailable(fallback)
-            } else {
-              GlideApp.with(this)
-                .loadWithFallbacks(urls)
-                ?.placeholder(fallback)
-                ?.into(object : SimpleTarget<Drawable>(432, 432) {
-                  override fun onResourceReady(resource: Drawable,
-                                               transition: Transition<in Drawable>?) {
-                    resultAvailable(resource)
-                  }
-
-                  override fun onLoadFailed(errorDrawable: Drawable?) {
-                    resultAvailable(errorDrawable!!)
-                  }
-                })
-            }
-          } else {
-            callback(IconCompat.createWithResource(this, R.drawable.ic_shortcut_channel))
-          }
-        }
-      }
-      true
-    }
     R.id.action_core_settings   -> {
       CoreSettingsActivity.launch(this)
       true
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/channel/ChannelInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/channel/ChannelInfoFragment.kt
index 0c11ee95d7151d3c00fcb278deb7b85dcce3135e..4cd9e9043605de3627365cb1b93590ed6b0d86c0 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/channel/ChannelInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/channel/ChannelInfoFragment.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.quasseldroid.ui.chat.info.channel
 
+import android.os.Build
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
@@ -29,10 +30,13 @@ import androidx.lifecycle.Observer
 import butterknife.BindView
 import butterknife.ButterKnife
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.topic.TopicActivity
+import de.kuschku.quasseldroid.util.ShortcutCreationHelper
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
@@ -60,9 +64,15 @@ class ChannelInfoFragment : ServiceBoundFragment() {
   @BindView(R.id.action_join)
   lateinit var actionJoin: Button
 
+  @BindView(R.id.action_shortcut)
+  lateinit var actionShortcut: Button
+
   @Inject
   lateinit var contentFormatter: ContentFormatter
 
+  @Inject
+  lateinit var messageSettings: MessageSettings
+
   override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                             savedInstanceState: Bundle?): View? {
     val view = inflater.inflate(R.layout.fragment_info_channel, container, false)
@@ -70,50 +80,74 @@ class ChannelInfoFragment : ServiceBoundFragment() {
 
     val openBuffer = arguments?.getBoolean("openBuffer")
 
+    var currentBufferInfo: BufferInfo? = null
+
     combineLatest(viewModel.session, viewModel.networks).map { (sessionOptional, networks) ->
       if (openBuffer == true) {
         val session = sessionOptional?.orNull()
         val bufferSyncer = session?.bufferSyncer
         val bufferInfo = bufferSyncer?.bufferInfo(arguments?.getInt("bufferId") ?: -1)
-        bufferInfo?.let {
-          networks[it.networkId]?.ircChannel(it.bufferName)
+        bufferInfo?.let { info ->
+          networks[info.networkId]?.ircChannel(info.bufferName)?.let {
+            Pair(info, it)
+          }
         }
       } else {
-        networks[arguments?.getInt("networkId")]?.ircChannel(arguments?.getString("nick"))
-      } ?: IrcChannel.NULL
+        networks[arguments?.getInt("networkId")]?.ircChannel(arguments?.getString("nick"))?.let {
+          Pair(null, it)
+        }
+      } ?: Pair(null, IrcChannel.NULL)
     }.filter {
-      it != IrcChannel.NULL
-    }.switchMap(IrcChannel::updates).toLiveData().observe(this, Observer { channel ->
-      if (channel != null) {
-        name.text = channel.name()
-        topic.text = contentFormatter.formatContent(channel.topic(),
-                                                    networkId = channel.network().networkId())
-
-        actionEditTopic.setOnClickListener {
-          TopicActivity.launch(requireContext(), buffer = arguments?.getInt("bufferId") ?: -1)
+      it.second != IrcChannel.NULL
+    }.switchMap { (info, channel) ->
+      channel.updates().map {
+        Pair(info, it)
+      }
+    }.toLiveData().observe(this, Observer { (info, channel) ->
+      name.text = channel.name()
+      topic.text = contentFormatter.formatContent(channel.topic(),
+                                                  networkId = channel.network().networkId())
+
+      currentBufferInfo = info
+      actionShortcut.visibleIf(info != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+
+      actionEditTopic.setOnClickListener {
+        TopicActivity.launch(requireContext(), buffer = arguments?.getInt("bufferId") ?: -1)
+      }
+
+      actionPart.setOnClickListener {
+        viewModel.session.value?.orNull()?.let { session ->
+          session.bufferSyncer?.find(
+            networkId = channel.network().networkId(),
+            type = Buffer_Type.of(Buffer_Type.StatusBuffer)
+          )?.let { statusInfo ->
+            session.rpcHandler?.sendInput(statusInfo, "/part ${channel.name()}")
+            requireActivity().finish()
+          }
         }
+      }
 
-        actionPart.setOnClickListener {
-          viewModel.session.value?.orNull()?.let { session ->
-            session.bufferSyncer?.find(
-              networkId = channel.network().networkId(),
-              type = Buffer_Type.of(Buffer_Type.StatusBuffer)
-            )?.let { statusInfo ->
-              session.rpcHandler?.sendInput(statusInfo, "/part ${channel.name()}")
-              requireActivity().finish()
-            }
+      actionWho.setOnClickListener {
+        viewModel.session.value?.orNull()?.let { session ->
+          session.bufferSyncer?.find(
+            networkId = channel.network().networkId(),
+            type = Buffer_Type.of(Buffer_Type.StatusBuffer)
+          )?.let { statusInfo ->
+            session.rpcHandler?.sendInput(statusInfo, "/who ${channel.name()}")
+            requireActivity().finish()
           }
         }
+      }
 
-        actionWho.setOnClickListener {
-          viewModel.session.value?.orNull()?.let { session ->
-            session.bufferSyncer?.find(
-              networkId = channel.network().networkId(),
-              type = Buffer_Type.of(Buffer_Type.StatusBuffer)
-            )?.let { statusInfo ->
-              session.rpcHandler?.sendInput(statusInfo, "/who ${channel.name()}")
-              requireActivity().finish()
-            }
+      actionShortcut.setOnClickListener {
+        this.context?.let { context ->
+          currentBufferInfo?.let { info ->
+            ShortcutCreationHelper.create(
+              context = context,
+              messageSettings = messageSettings,
+              accountId = accountId,
+              info = info
+            )
           }
         }
       }
@@ -159,6 +193,15 @@ class ChannelInfoFragment : ServiceBoundFragment() {
     )
     actionPart.retint()
 
+    actionShortcut.setTooltip()
+    actionShortcut.setCompoundDrawablesWithIntrinsicBounds(
+      null,
+      requireContext().getVectorDrawableCompat(R.drawable.ic_link),
+      null,
+      null
+    )
+    actionShortcut.retint()
+
     return view
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/IrcUserInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/IrcUserInfo.kt
index 520b9d3d1c944879f242421a0141a9c804ee20d4..f49fc0a9696ecdebf4d297eb6ae31e8fc9443904 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/IrcUserInfo.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/IrcUserInfo.kt
@@ -19,6 +19,8 @@
 
 package de.kuschku.quasseldroid.ui.chat.info.user
 
+import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.quassel.syncables.Network
 
 data class IrcUserInfo(
@@ -32,5 +34,7 @@ data class IrcUserInfo(
   val isAway: Boolean? = false,
   val awayMessage: String? = null,
   val network: Network? = null,
-  val knownToCore: Boolean = false
+  val knownToCore: Boolean = false,
+  val info: BufferInfo? = null,
+  val ircUser: IrcUser? = null
 )
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 dec3b63d18690f8428c37eb232177766f8fcc037..858c298f47d6a800a0c1d358bde3804108adcd72 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
@@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.ui.chat.info.user
 
 import android.content.Intent
 import android.net.Uri
+import android.os.Build
 import android.os.Bundle
 import android.text.SpannableString
 import android.view.LayoutInflater
@@ -42,6 +43,7 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
+import de.kuschku.quasseldroid.util.ShortcutCreationHelper
 import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.avatars.MatrixApi
 import de.kuschku.quasseldroid.util.avatars.MatrixAvatarInfo
@@ -77,6 +79,9 @@ class UserInfoFragment : ServiceBoundFragment() {
   @BindView(R.id.action_mention)
   lateinit var actionMention: Button
 
+  @BindView(R.id.action_shortcut)
+  lateinit var actionShortcut: Button
+
   @BindView(R.id.away_container)
   lateinit var awayContainer: ViewGroup
 
@@ -127,27 +132,41 @@ class UserInfoFragment : ServiceBoundFragment() {
 
     val networkId2 = arguments?.getInt("networkId")
     val nickName2 = arguments?.getString("nick")
+
+    var currentBufferInfo: BufferInfo? = null
+    var currentIrcUser: IrcUser? = null
+
+    fun updateShortcutVisibility() {
+      actionShortcut.visibleIf(currentBufferInfo != null)
+    }
+
     combineLatest(viewModel.session, viewModel.networks).switchMap { (sessionOptional, networks) ->
-      fun processUser(user: IrcUser, info: BufferInfo? = null) = when {
-        user == IrcUser.NULL && info != null -> Optional.of(IrcUserInfo(
-          networkId = info.networkId,
-          nick = info.bufferName ?: "",
-          knownToCore = true
-        ))
-        user == IrcUser.NULL                 -> Optional.empty()
-        else                                 -> Optional.of(IrcUserInfo(
-          networkId = user.network().networkId(),
-          nick = user.nick(),
-          user = user.user(),
-          host = user.host(),
-          account = user.account(),
-          server = user.server(),
-          realName = user.realName(),
-          isAway = user.isAway(),
-          awayMessage = user.awayMessage(),
-          network = user.network(),
-          knownToCore = true
-        ))
+      fun processUser(user: IrcUser, info: BufferInfo? = null): Optional<IrcUserInfo> {
+        updateShortcutVisibility()
+        return when {
+          user == IrcUser.NULL && info != null -> Optional.of(IrcUserInfo(
+            networkId = info.networkId,
+            nick = info.bufferName ?: "",
+            knownToCore = true,
+            info = info
+          ))
+          user == IrcUser.NULL                 -> Optional.empty()
+          else                                 -> Optional.of(IrcUserInfo(
+            networkId = user.network().networkId(),
+            nick = user.nick(),
+            user = user.user(),
+            host = user.host(),
+            account = user.account(),
+            server = user.server(),
+            realName = user.realName(),
+            isAway = user.isAway(),
+            awayMessage = user.awayMessage(),
+            network = user.network(),
+            knownToCore = true,
+            info = info,
+            ircUser = user
+          ))
+        }
       }
 
       if (openBuffer == true) {
@@ -166,7 +185,12 @@ class UserInfoFragment : ServiceBoundFragment() {
           ?.map { user -> processUser(user) }
       } ?: Observable.just(IrcUser.NULL).map { user -> processUser(user) }
     }.toLiveData().observe(this, Observer {
-      val processUser = { user: IrcUserInfo ->
+      val user = it.orNull()
+      if (user != null) {
+        currentBufferInfo = user.info
+        currentIrcUser = user.ircUser
+        actionShortcut.visibleIf(currentBufferInfo != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+
         avatar.post {
           avatar.visibility = View.GONE
           actualUrl = null
@@ -279,8 +303,21 @@ class UserInfoFragment : ServiceBoundFragment() {
             }
           }
         }
+
+        actionShortcut.setOnClickListener {
+          this.context?.let { context ->
+            currentBufferInfo?.let { info ->
+              ShortcutCreationHelper.create(
+                context = context,
+                messageSettings = messageSettings,
+                accountId = accountId,
+                info = info,
+                ircUser = currentIrcUser
+              )
+            }
+          }
+        }
       }
-      it?.orNull()?.let(processUser)
     })
 
     avatar.setOnClickListener {
@@ -331,6 +368,15 @@ class UserInfoFragment : ServiceBoundFragment() {
     )
     actionMention.retint()
 
+    actionShortcut.setTooltip()
+    actionShortcut.setCompoundDrawablesWithIntrinsicBounds(
+      null,
+      requireContext().getVectorDrawableCompat(R.drawable.ic_link),
+      null,
+      null
+    )
+    actionShortcut.retint()
+
     return view
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..81f14ce6771d435de6e1977c904d60fbca06ff25
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt
@@ -0,0 +1,130 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * context 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.
+ *
+ * context 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 context program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.graphics.drawable.IconCompat
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.transition.Transition
+import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.syncables.IrcUser
+import de.kuschku.libquassel.util.flag.hasFlag
+import de.kuschku.libquassel.util.irc.SenderColorUtil
+import de.kuschku.quasseldroid.GlideApp
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.settings.MessageSettings
+import de.kuschku.quasseldroid.ui.chat.ChatActivity
+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.viewmodel.EditorViewModel
+
+object ShortcutCreationHelper {
+  fun create(context: Context,
+             messageSettings: MessageSettings,
+             accountId: Long,
+             info: BufferInfo,
+             ircUser: IrcUser? = null) {
+    val callback: (IconCompat) -> Unit = { icon ->
+      ShortcutManagerCompat.requestPinShortcut(
+        context,
+        ShortcutInfoCompat.Builder(context, "${System.currentTimeMillis()}")
+          .setShortLabel(info.bufferName ?: "")
+          .setIcon(icon)
+          .setIntent(
+            ChatActivity.intent(
+              context,
+              bufferId = info.bufferId,
+              accountId = accountId
+            ).setAction(Intent.ACTION_VIEW)
+          )
+          .build(),
+        null
+      )
+    }
+
+    val resultAvailable: (Drawable) -> Unit = { resource ->
+      val bitmap = Bitmap.createBitmap(432, 432, Bitmap.Config.ARGB_8888)
+      val canvas = Canvas(bitmap)
+      resource.setBounds(0, 0, canvas.width, canvas.height)
+      resource.draw(canvas)
+      callback(IconCompat.createWithAdaptiveBitmap(bitmap))
+    }
+
+    val senderColors = context.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 colorContext = ColorContext(context, messageSettings)
+
+    if (info.type.hasFlag(Buffer_Type.QueryBuffer)) {
+      val nickName = info.bufferName ?: ""
+      val senderColorIndex = SenderColorUtil.senderColor(nickName)
+      val rawInitial = nickName.trimStart(*EditorViewModel.IGNORED_CHARS).firstOrNull()
+                       ?: nickName.firstOrNull()
+      val initial = rawInitial?.toUpperCase().toString()
+      val senderColor = senderColors[senderColorIndex]
+
+      val fallback = colorContext.prepareTextDrawable()
+        .beginConfig()
+        .scale(0.5f)
+        .endConfig()
+        .buildRect(initial, senderColor)
+
+      val urls = ircUser?.let {
+        AvatarHelper.avatar(messageSettings, it, 432)
+      }
+
+      if (urls == null || urls.isEmpty()) {
+        resultAvailable(fallback)
+      } else {
+        GlideApp.with(context)
+          .loadWithFallbacks(urls)
+          ?.placeholder(fallback)
+          ?.into(object : SimpleTarget<Drawable>(432, 432) {
+            override fun onResourceReady(resource: Drawable,
+                                         transition: Transition<in Drawable>?) {
+              resultAvailable(resource)
+            }
+
+            override fun onLoadFailed(errorDrawable: Drawable?) {
+              resultAvailable(errorDrawable!!)
+            }
+          })
+      }
+    } else {
+      callback(IconCompat.createWithResource(context, R.drawable.ic_shortcut_channel))
+    }
+  }
+}
diff --git a/app/src/main/res/drawable/ic_link.xml b/app/src/main/res/drawable/ic_link.xml
new file mode 100644
index 0000000000000000000000000000000000000000..640faf78788fb2c35e29e75b8fd05d6617a8fb67
--- /dev/null
+++ b/app/src/main/res/drawable/ic_link.xml
@@ -0,0 +1,28 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2019 Janne Koschinski
+  Copyright (c) 2019 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program. If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="24dp"
+  android:height="24dp"
+  android:viewportWidth="24"
+  android:viewportHeight="24">
+  <path
+    android:fillColor="#000"
+    android:pathData="M3.9,12C3.9,10.29 5.29,8.9 7,8.9H11V7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H11V15.1H7C5.29,15.1 3.9,13.71 3.9,12M8,13H16V11H8V13M17,7H13V8.9H17C18.71,8.9 20.1,10.29 20.1,12C20.1,13.71 18.71,15.1 17,15.1H13V17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7Z" />
+</vector>
diff --git a/app/src/main/res/layout/fragment_info_channel.xml b/app/src/main/res/layout/fragment_info_channel.xml
index eb77031e873e0790ed7787f16b8d8aaea3147517..1e0a4ad5c79ad3e00f8c914b33f5293da0d5be1b 100644
--- a/app/src/main/res/layout/fragment_info_channel.xml
+++ b/app/src/main/res/layout/fragment_info_channel.xml
@@ -66,6 +66,7 @@
           style="@style/Widget.Info.ActionButton"
           android:contentDescription="@string/label_edit_topic_long"
           android:text="@string/label_edit_topic"
+          tools:drawableTop="@drawable/ic_pencil"
           tools:drawableTint="?colorTextSecondary" />
 
         <androidx.appcompat.widget.AppCompatButton
@@ -73,23 +74,34 @@
           style="@style/Widget.Info.ActionButton"
           android:contentDescription="@string/label_who_long"
           android:text="@string/label_who"
+          tools:drawableTop="@drawable/ic_info"
           tools:drawableTint="?colorTextSecondary" />
 
+        <androidx.appcompat.widget.AppCompatButton
+          android:id="@+id/action_join"
+          style="@style/Widget.Info.ActionButton"
+          android:contentDescription="@string/label_join_long"
+          android:text="@string/label_join"
+          android:visibility="gone"
+          tools:drawableTint="?colorTextSecondary"
+          tools:drawableTop="@drawable/ic_account_plus"
+          tools:visibility="visible" />
+
         <androidx.appcompat.widget.AppCompatButton
           android:id="@+id/action_part"
           style="@style/Widget.Info.ActionButton"
           android:contentDescription="@string/label_part_long"
           android:text="@string/label_part"
+          tools:drawableTop="@drawable/ic_account_minus"
           tools:drawableTint="?colorTextSecondary" />
 
         <androidx.appcompat.widget.AppCompatButton
-          android:id="@+id/action_join"
+          android:id="@+id/action_shortcut"
           style="@style/Widget.Info.ActionButton"
-          android:contentDescription="@string/label_join_long"
-          android:text="@string/label_join"
-          android:visibility="gone"
+          android:contentDescription="@string/label_shortcut_long"
+          android:text="@string/label_shortcut"
           tools:drawableTint="?colorTextSecondary"
-          tools:visibility="visible" />
+          tools:drawableTop="@drawable/ic_link" />
       </LinearLayout>
     </HorizontalScrollView>
 
diff --git a/app/src/main/res/layout/fragment_info_user.xml b/app/src/main/res/layout/fragment_info_user.xml
index 95d0c930723aff25b489afe162552e78b19d9147..8f78aa6fa7f1f8c96efebdf5538ca95e9a634847 100644
--- a/app/src/main/res/layout/fragment_info_user.xml
+++ b/app/src/main/res/layout/fragment_info_user.xml
@@ -107,6 +107,7 @@
             style="@style/Widget.Info.ActionButton"
             android:contentDescription="@string/label_query_long"
             android:text="@string/label_query"
+            tools:drawableTop="@drawable/ic_message_bulleted"
             tools:drawableTint="?colorTextSecondary" />
 
           <androidx.appcompat.widget.AppCompatButton
@@ -115,6 +116,7 @@
             android:contentDescription="@string/label_ignore_long"
             android:text="@string/label_ignore"
             android:visibility="gone"
+            tools:drawableTop="@drawable/ic_eye_off"
             tools:drawableTint="?colorTextSecondary"
             tools:visibility="visible" />
 
@@ -123,6 +125,7 @@
             style="@style/Widget.Info.ActionButton"
             android:contentDescription="@string/label_whois_long"
             android:text="@string/label_whois"
+            tools:drawableTop="@drawable/ic_info"
             tools:drawableTint="?colorTextSecondary" />
 
           <androidx.appcompat.widget.AppCompatButton
@@ -130,7 +133,16 @@
             style="@style/Widget.Info.ActionButton"
             android:contentDescription="@string/label_mention_long"
             android:text="@string/label_mention"
+            tools:drawableTop="@drawable/ic_share_alternative"
             tools:drawableTint="?colorTextSecondary" />
+
+          <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/action_shortcut"
+            style="@style/Widget.Info.ActionButton"
+            android:contentDescription="@string/label_shortcut_long"
+            android:text="@string/label_shortcut"
+            tools:drawableTint="?colorTextSecondary"
+            tools:drawableTop="@drawable/ic_link" />
         </LinearLayout>
       </HorizontalScrollView>
 
diff --git a/app/src/main/res/menu/activity_main.xml b/app/src/main/res/menu/activity_main.xml
index 2851fa892125726c6e28fda002ad941f51c78e66..c5b8aa5d50916945527b4a1c967141b98ee51559 100644
--- a/app/src/main/res/menu/activity_main.xml
+++ b/app/src/main/res/menu/activity_main.xml
@@ -27,9 +27,6 @@
   <item
     android:id="@+id/action_filter_messages"
     android:title="@string/label_filter_messages" />
-  <item
-    android:id="@+id/action_create_shortcut"
-    android:title="@string/label_create_shortcut" />
   <item
     android:id="@+id/action_core_settings"
     android:title="@string/label_settings_core" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 52df2bb65bdff43ce3f8c322be939674ee495c37..9ecd98da9398a1d11e59c33476dc8d216ded3f69 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -44,7 +44,8 @@
   <string name="label_contributors">Contributors</string>
   <string name="label_copy">Copy</string>
   <string name="label_crashes">Crashes</string>
-  <string name="label_create_shortcut">Create Shortcut</string>
+  <string name="label_shortcut">Shortcut</string>
+  <string name="label_shortcut_long">Create Shortcut on Homescreen</string>
   <string name="label_delete">Delete</string>
   <string name="label_delete_all">Delete All</string>
   <string name="label_disconnect">Disconnect</string>