Эх сурвалжийг харах

Federated Mentions

- Federated mention chip
- Federated message avatars
- Helper functions

Signed-off-by: Julius Linus <julius.linus@nextcloud.com>
Julius Linus 1 жил өмнө
parent
commit
870ef03d61
16 өөрчлөгдсөн 181 нэмэгдсэн , 36 устгасан
  1. 43 15
      app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java
  2. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt
  3. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt
  4. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt
  5. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt
  6. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt
  7. 3 0
      app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt
  8. 14 7
      app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java
  9. 2 0
      app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
  10. 42 0
      app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt
  11. 3 1
      app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt
  12. 7 2
      app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt
  13. 6 0
      app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java
  14. 13 0
      app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt
  15. 22 9
      app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
  16. 11 2
      app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt

+ 43 - 15
app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java

@@ -58,8 +58,10 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
     public static final String SOURCE_GUESTS = "guests";
 
     public static final String SOURCE_GROUPS = "groups";
+    public static final String SOURCE_FEDERATION = "federated_users";
 
     private String source;
+    private final String mentionId;
     private final String objectId;
     private final String displayName;
     private final String status;
@@ -67,12 +69,14 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
     private final String statusMessage;
     private final User currentUser;
     private final Context context;
+    private final String roomToken;
     private final ViewThemeUtils viewThemeUtils;
 
     public MentionAutocompleteItem(
         Mention mention,
         User currentUser,
-        Context activityContext, ViewThemeUtils viewThemeUtils) {
+        Context activityContext, String roomToken, ViewThemeUtils viewThemeUtils) {
+        this.mentionId = mention.getMentionId();
         this.objectId = mention.getId();
         this.displayName = mention.getLabel();
         this.source = mention.getSource();
@@ -82,6 +86,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
         this.currentUser = currentUser;
         this.context = activityContext;
         this.viewThemeUtils = viewThemeUtils;
+        this.roomToken = roomToken;
     }
 
     public String getSource() {
@@ -92,6 +97,10 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
         this.source = source;
     }
 
+    public String getMentionId() {
+        return mentionId;
+    }
+
     public String getObjectId() {
         return objectId;
     }
@@ -100,6 +109,10 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
         return displayName;
     }
 
+    public String getRoomToken() {
+        return roomToken;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof MentionAutocompleteItem inItem) {
@@ -147,24 +160,39 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
             holder.binding.secondaryText.setText("@" + objectId);
         }
 
-        if (SOURCE_CALLS.equals(source) || SOURCE_GROUPS.equals(source)) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                ImageViewExtensionsKt.loadUserAvatar(
-                    holder.binding.avatarView,
-                    viewThemeUtils.talk.themePlaceholderAvatar(
+        String avatarId = objectId;
+        switch (source) {
+            case SOURCE_CALLS: {}
+            case SOURCE_GROUPS: {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                    ImageViewExtensionsKt.loadUserAvatar(
                         holder.binding.avatarView,
-                        R.drawable.ic_avatar_group
-                                                              )
-                                                    );
-            } else {
-                ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, R.drawable.ic_circular_group);
+                        viewThemeUtils.talk.themePlaceholderAvatar(
+                            holder.binding.avatarView,
+                            R.drawable.ic_avatar_group));
+                } else {
+                    ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, R.drawable.ic_circular_group);
+                }
+                break;
             }
-        } else {
-            String avatarId = objectId;
-            if (SOURCE_GUESTS.equals(source)) {
+            case SOURCE_FEDERATION: {
+                int darkTheme = (DisplayUtils.isDarkModeOn(this.context))? 1 : 0;
+                ImageViewExtensionsKt.loadFederatedUserAvatar(holder.binding.avatarView,
+                                                              currentUser,
+                                                              Objects.requireNonNull(currentUser.getBaseUrl()),
+                                                              roomToken,
+                                                              avatarId,
+                                                              darkTheme,
+                                                              true,
+                                                              false);
+                break;
+            }
+            case SOURCE_GUESTS: {
                 avatarId = displayName;
             }
-            ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, currentUser, avatarId, true, false);
+            default: {
+                ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, currentUser, avatarId, true, false);
+            }
         }
 
         drawStatus(holder);

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt

@@ -37,6 +37,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
 import com.nextcloud.talk.databinding.ItemCustomIncomingLinkPreviewMessageBinding
 import com.nextcloud.talk.extensions.loadBotsAvatar
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -172,6 +173,8 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
             binding.messageUserAvatar.loadChangelogBotAvatar()
         } else if (message.actorType == "bots") {
             binding.messageUserAvatar.loadBotsAvatar()
+        } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+            binding.messageUserAvatar.loadFederatedUserAvatar(message)
         }
     }
 

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt

@@ -47,6 +47,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
 import com.nextcloud.talk.databinding.ItemCustomIncomingLocationMessageBinding
 import com.nextcloud.talk.extensions.loadBotsAvatar
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -148,6 +149,8 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) :
                 binding.messageUserAvatar.loadChangelogBotAvatar()
             } else if (message.actorType == "bots") {
                 binding.messageUserAvatar.loadBotsAvatar()
+            } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+                binding.messageUserAvatar.loadFederatedUserAvatar(message)
             }
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt

@@ -36,6 +36,7 @@ import com.nextcloud.talk.chat.ChatActivity
 import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding
 import com.nextcloud.talk.extensions.loadBotsAvatar
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.polls.ui.PollMainDialogFragment
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
@@ -179,6 +180,8 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
             binding.messageUserAvatar.loadChangelogBotAvatar()
         } else if (message.actorType == "bots") {
             binding.messageUserAvatar.loadBotsAvatar()
+        } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+            binding.messageUserAvatar.loadFederatedUserAvatar(message)
         }
     }
 

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt

@@ -41,6 +41,7 @@ import com.nextcloud.talk.chat.ChatActivity
 import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
 import com.nextcloud.talk.extensions.loadBotsAvatar
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -182,6 +183,8 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
             binding.messageUserAvatar.loadChangelogBotAvatar()
         } else if (message.actorType == "bots") {
             binding.messageUserAvatar.loadBotsAvatar()
+        } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+            binding.messageUserAvatar.loadFederatedUserAvatar(message)
         }
     }
 

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt

@@ -45,6 +45,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
 import com.nextcloud.talk.databinding.ItemCustomIncomingVoiceMessageBinding
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -285,6 +286,8 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
                 )
             binding.messageUserAvatar.visibility = View.VISIBLE
             binding.messageUserAvatar.setImageDrawable(drawable)
+        } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+            binding.messageUserAvatar.loadFederatedUserAvatar(message)
         }
     }
 

+ 3 - 0
app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt

@@ -49,6 +49,7 @@ import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.users.UserManager
@@ -194,6 +195,8 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
                 }
                 if (ACTOR_TYPE_BOTS == message.actorType && ACTOR_ID_CHANGELOG == message.actorId) {
                     userAvatar.loadChangelogBotAvatar()
+                } else if (message.actorType == "federated_users" && message.messageParameters?.get("actor") != null) {
+                    userAvatar.loadFederatedUserAvatar(message)
                 }
             }
         }

+ 14 - 7
app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java

@@ -27,7 +27,6 @@ import android.text.Editable;
 import android.text.Spanned;
 import android.widget.EditText;
 
-import third.parties.fresco.BetterImageSpan;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.data.user.model.User;
 import com.nextcloud.talk.models.json.mention.Mention;
@@ -39,7 +38,10 @@ import com.otaliastudios.autocomplete.AutocompleteCallback;
 import com.vanniktech.emoji.EmojiRange;
 import com.vanniktech.emoji.Emojis;
 
+import java.util.Objects;
+
 import kotlin.OptIn;
+import third.parties.fresco.BetterImageSpan;
 
 public class MentionAutocompleteCallback implements AutocompleteCallback<Mention> {
     private final ViewThemeUtils viewThemeUtils;
@@ -66,26 +68,31 @@ public class MentionAutocompleteCallback implements AutocompleteCallback<Mention
         }
         String replacement = item.getLabel();
 
-        StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel());
+        StringBuilder replacementStringBuilder = new StringBuilder(Objects.requireNonNull(item.getLabel()));
         for (EmojiRange emojiRange : Emojis.emojis(replacement)) {
             replacementStringBuilder.delete(emojiRange.range.getStart(), emojiRange.range.getEndInclusive());
         }
 
-        editable.replace(range.getStart(), range.getEnd(), replacementStringBuilder + " ");
+        String charSequence = " ";
+        editable.replace(range.getStart(), range.getEnd(), charSequence + replacementStringBuilder + " ");
+        String id;
+        if (item.getMentionId() != null) id = item.getMentionId(); else id = item.getId();
         Spans.MentionChipSpan mentionChipSpan =
             new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context,
                                                                                  item.getId(),
+                                                                                 item.getRoomToken(),
                                                                                  item.getLabel(),
                                                                                  conversationUser,
                                                                                  item.getSource(),
                                                                                  R.xml.chip_you,
                                                                                  editText,
-                                                                                 viewThemeUtils),
+                                                                                 viewThemeUtils,
+                                                                                 "federated_users".equals(item.getSource())),
                                       BetterImageSpan.ALIGN_CENTER,
-                                      item.getId(), item.getLabel());
+                                      id, item.getLabel());
         editable.setSpan(mentionChipSpan,
-                         range.getStart(),
-                         range.getStart() + replacementStringBuilder.length(),
+                         range.getStart() + charSequence.length(),
+                         range.getStart() + replacementStringBuilder.length() + charSequence.length(),
                          Spanned.SPAN_INCLUSIVE_INCLUSIVE);
 
 

+ 2 - 0
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

@@ -3584,6 +3584,7 @@ class ChatActivity :
                 mentionSpan = mentionSpans[i]
                 var mentionId = mentionSpan.id
                 if (mentionId.contains(" ") ||
+                    mentionId.contains("@") ||
                     mentionId.startsWith("guest/") ||
                     mentionId.startsWith("group/")
                 ) {
@@ -3798,6 +3799,7 @@ class ChatActivity :
             chatMessage.isFormerOneToOneConversation =
                 (currentConversation?.type == ConversationType.FORMER_ONE_TO_ONE)
             chatMessage.activeUser = conversationUser
+            chatMessage.roomToken = roomToken
         }
 
         if (adapter != null) {

+ 42 - 0
app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt

@@ -46,6 +46,7 @@ import com.nextcloud.talk.R
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.models.domain.ConversationModel
 import com.nextcloud.talk.models.domain.ConversationType
+import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.models.json.conversations.Conversation
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -126,6 +127,47 @@ fun ImageView.loadUserAvatar(
     return loadAvatarInternal(user, imageRequestUri, ignoreCache, null)
 }
 
+fun ImageView.loadFederatedUserAvatar(message: ChatMessage): io.reactivex.disposables.Disposable {
+    val map = message.messageParameters?.get("actor")
+    val url = map?.get("server")!!
+    val id = map["id"]
+    val cloudId = "$id@$url"
+    val darkTheme = if (DisplayUtils.isDarkModeOn(context)) 1 else 0
+    val ignoreCache = false
+    val requestBigSize = true
+    return loadFederatedUserAvatar(
+        message.activeUser!!,
+        message.activeUser!!.baseUrl!!,
+        message.roomToken,
+        cloudId,
+        darkTheme,
+        requestBigSize,
+        ignoreCache
+    )
+}
+
+@Suppress("LongParameterList")
+fun ImageView.loadFederatedUserAvatar(
+    user: User,
+    baseUrl: String,
+    token: String,
+    cloudId: String,
+    darkTheme: Int,
+    requestBigSize: Boolean = true,
+    ignoreCache: Boolean
+): io.reactivex.disposables.Disposable {
+    val imageRequestUri = ApiUtils.getUrlForFederatedAvatar(
+        baseUrl,
+        token,
+        cloudId,
+        darkTheme,
+        requestBigSize
+    )
+    Log.d("Julius", "URL::$imageRequestUri")
+
+    return loadAvatarInternal(user, imageRequestUri, ignoreCache, null)
+}
+
 @OptIn(ExperimentalCoilApi::class)
 private fun ImageView.loadAvatarInternal(
     user: User?,

+ 3 - 1
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt

@@ -159,7 +159,9 @@ data class ChatMessage(
 
     var hiddenByCollapse: Boolean = false,
 
-    var openWhenDownloaded: Boolean = true
+    var openWhenDownloaded: Boolean = true,
+
+    var roomToken: String = ""
 
 ) : Parcelable, MessageContentType, MessageContentType.Image {
 

+ 7 - 2
app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.kt

@@ -23,12 +23,15 @@ package com.nextcloud.talk.models.json.mention
 
 import android.os.Parcelable
 import com.bluelinelabs.logansquare.annotation.JsonField
+import com.bluelinelabs.logansquare.annotation.JsonIgnore
 import com.bluelinelabs.logansquare.annotation.JsonObject
 import kotlinx.parcelize.Parcelize
 
 @Parcelize
 @JsonObject
 data class Mention(
+    @JsonField(name = ["mentionId"])
+    var mentionId: String?,
     @JsonField(name = ["id"])
     var id: String?,
     @JsonField(name = ["label"])
@@ -41,8 +44,10 @@ data class Mention(
     @JsonField(name = ["statusIcon"])
     var statusIcon: String?,
     @JsonField(name = ["statusMessage"])
-    var statusMessage: String?
+    var statusMessage: String?,
+    @JsonIgnore
+    var roomToken: String?
 ) : Parcelable {
     // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
-    constructor() : this(null, null, null, null, null, null)
+    constructor() : this(null, null, null, null, null, null, null, null)
 }

+ 6 - 0
app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java

@@ -155,6 +155,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
                                                 mention,
                                                 currentUser,
                                                 context,
+                                                roomToken,
                                                 viewThemeUtils));
                             }
 
@@ -185,9 +186,14 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
         Mention mention = new Mention();
         MentionAutocompleteItem mentionAutocompleteItem = (MentionAutocompleteItem) adapter.getItem(position);
         if (mentionAutocompleteItem != null) {
+            String mentionId = mentionAutocompleteItem.getMentionId();
+            if(mentionId != null) {
+                mention.setMentionId(mentionId);
+            }
             mention.setId(mentionAutocompleteItem.getObjectId());
             mention.setLabel(mentionAutocompleteItem.getDisplayName());
             mention.setSource(mentionAutocompleteItem.getSource());
+            mention.setRoomToken(mentionAutocompleteItem.getRoomToken());
             dispatchClick(mention);
         }
         return true;

+ 13 - 0
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt

@@ -383,6 +383,19 @@ object ApiUtils {
         return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize
     }
 
+    @JvmStatic
+    fun getUrlForFederatedAvatar(
+        baseUrl: String,
+        token: String,
+        cloudId: String,
+        darkTheme: Int,
+        requestBigSize: Boolean
+    ): String {
+        val avatarSize = if (requestBigSize) AVATAR_SIZE_BIG else AVATAR_SIZE_SMALL
+        val url = "$baseUrl$OCS_API_VERSION$SPREED_API_VERSION/proxy/$token/user-avatar/$avatarSize"
+        return "$url?cloudId=$cloudId&darkTheme=$darkTheme"
+    }
+
     @JvmStatic
     fun getUrlForGuestAvatar(baseUrl: String?, name: String?, requestBigSize: Boolean): String {
         val avatarSize = if (requestBigSize) AVATAR_SIZE_BIG else AVATAR_SIZE_SMALL

+ 22 - 9
app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java

@@ -68,6 +68,7 @@ import org.greenrobot.eventbus.EventBus;
 
 import java.text.DateFormat;
 import java.util.Date;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -168,12 +169,14 @@ public class DisplayUtils {
 
     public static Drawable getDrawableForMentionChipSpan(Context context,
                                                          String id,
+                                                         String roomToken,
                                                          CharSequence label,
                                                          User conversationUser,
                                                          String type,
                                                          @XmlRes int chipResource,
                                                          @Nullable EditText emojiEditText,
-                                                         ViewThemeUtils viewThemeUtils) {
+                                                         ViewThemeUtils viewThemeUtils,
+                                                         Boolean isFederated) {
         ChipDrawable chip = ChipDrawable.createFromResource(context, chipResource);
         chip.setText(EmojiCompat.get().process(label));
         chip.setEllipsize(TextUtils.TruncateAt.MIDDLE);
@@ -205,13 +208,21 @@ public class DisplayUtils {
         chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight());
 
         if (!isCallOrGroup) {
-            String url = ApiUtils.getUrlForAvatar(conversationUser.getBaseUrl(), id, true);
+            String url = ApiUtils.getUrlForAvatar(conversationUser.getBaseUrl(), id, false);
             if ("guests".equals(type) || "guest".equals(type)) {
                 url = ApiUtils.getUrlForGuestAvatar(
                     conversationUser.getBaseUrl(),
                     String.valueOf(label), true);
             }
 
+            if (isFederated) {
+                int darkTheme = (DisplayUtils.isDarkModeOn(context))? 1 : 0;
+                url = ApiUtils.getUrlForFederatedAvatar(Objects.requireNonNull(conversationUser.getBaseUrl()),
+                                                        roomToken, id,
+                                                        darkTheme, false);
+            }
+
+
             ImageRequest imageRequest = new ImageRequest.Builder(context)
                 .data(url)
                 .crossfade(true)
@@ -224,13 +235,12 @@ public class DisplayUtils {
 
                     @Override
                     public void onError(@Nullable Drawable drawable) {
-
+                        chip.setChipIcon(drawable);
                     }
 
                     @Override
                     public void onSuccess(@NonNull Drawable drawable) {
                         chip.setChipIcon(drawable);
-
                         // A hack to refresh the chip icon
                         if (emojiEditText != null) {
                             emojiEditText.post(() -> emojiEditText.setTextKeepState(
@@ -248,10 +258,12 @@ public class DisplayUtils {
     }
 
     public static Spannable searchAndReplaceWithMentionSpan(String key, Context context, Spanned text,
-                                                            String id, String label, String type,
+                                                            String id, String roomToken,
+                                                            String label, String type,
                                                             User conversationUser,
                                                             @XmlRes int chipXmlRes,
-                                                            ViewThemeUtils viewThemeUtils) {
+                                                            ViewThemeUtils viewThemeUtils,
+                                                            Boolean isFederated) {
 
         Spannable spannableString = new SpannableString(text);
         String stringText = text.toString();
@@ -267,7 +279,7 @@ public class DisplayUtils {
             }
         };
 
-        int lastStartIndex = -1;
+        int lastStartIndex = 0;
         Spans.MentionChipSpan mentionChipSpan;
         while (m.find()) {
             int start = stringText.indexOf(m.group(), lastStartIndex);
@@ -276,13 +288,14 @@ public class DisplayUtils {
 
             Drawable drawableForChip = DisplayUtils.getDrawableForMentionChipSpan(context,
                                                                                   id,
+                                                                                  roomToken,
                                                                                   label,
                                                                                   conversationUser,
                                                                                   type,
                                                                                   chipXmlRes,
                                                                                   null,
-                                                                                  viewThemeUtils);
-
+                                                                                  viewThemeUtils,
+                                                                                  isFederated);
             mentionChipSpan = new Spans.MentionChipSpan(drawableForChip,
                                                         BetterImageSpan.ALIGN_CENTER,
                                                         id,

+ 11 - 2
app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt

@@ -127,16 +127,24 @@ class MessageUtils(val context: Context) {
                         } else {
                             R.xml.chip_others
                         }
+                        val id = if (individualHashMap["server"] != null) {
+                            individualHashMap["id"] + "@" + individualHashMap["server"]
+                        } else {
+                            individualHashMap["id"]
+                        }
+
                         messageStringInternal = DisplayUtils.searchAndReplaceWithMentionSpan(
                             key,
                             themingContext,
                             messageStringInternal,
-                            individualHashMap["id"]!!,
+                            id,
+                            message.roomToken,
                             individualHashMap["name"]!!,
                             individualHashMap["type"]!!,
                             message.activeUser!!,
                             chip,
-                            viewThemeUtils
+                            viewThemeUtils,
+                            individualHashMap["server"] != null
                         )
                     }
 
@@ -174,5 +182,6 @@ class MessageUtils(val context: Context) {
     companion object {
         private const val TAG = "MessageUtils"
         const val MAX_REPLY_LENGTH = 250
+        const val HTTPS_PROTOCOL = "https://"
     }
 }