Browse Source

Merge pull request #4473 from nextcloud/feature/4363/displayEmailGuests

Feature/4363/display email guests
Marcel Hibbe 7 months ago
parent
commit
a1315d3689
18 changed files with 152 additions and 106 deletions
  1. 4 0
      app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.java
  2. 10 1
      app/src/main/java/com/nextcloud/talk/adapters/ParticipantsAdapter.java
  3. 5 4
      app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt
  4. 21 14
      app/src/main/java/com/nextcloud/talk/adapters/items/ParticipantItem.kt
  5. 2 14
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingDeckCardViewHolder.kt
  6. 2 14
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt
  7. 2 14
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt
  8. 4 25
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt
  9. 2 14
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt
  10. 2 1
      app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt
  11. 1 1
      app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt
  12. 54 0
      app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt
  13. 1 1
      app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt
  14. 3 0
      app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt
  15. 37 0
      app/src/main/java/com/nextcloud/talk/utils/ChatMessageUtils.kt
  16. 1 1
      app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt
  17. 1 1
      app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt
  18. 0 1
      app/src/main/res/layout/rv_item_conversation_info_participant.xml

+ 4 - 0
app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.java

@@ -172,6 +172,10 @@ public class ParticipantDisplayItem {
         return raisedHand;
     }
 
+    public Participant.ActorType getActorType() {
+        return actorType;
+    }
+
     public void addObserver(Observer observer) {
         participantDisplayItemNotifier.addObserver(observer);
     }

+ 10 - 1
app/src/main/java/com/nextcloud/talk/adapters/ParticipantsAdapter.java

@@ -22,6 +22,7 @@ import android.widget.TextView;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.activities.CallActivity;
 import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
+import com.nextcloud.talk.models.json.participants.Participant;
 
 import org.webrtc.MediaStream;
 import org.webrtc.MediaStreamTrack;
@@ -143,7 +144,15 @@ public class ParticipantsAdapter extends BaseAdapter {
                 nickTextView.setVisibility(View.VISIBLE);
                 nickTextView.setText(participantDisplayItem.getNick());
             }
-            ImageViewExtensionsKt.loadAvatarWithUrl(imageView,null, participantDisplayItem.getUrlForAvatar());
+            if (participantDisplayItem.getActorType() == Participant.ActorType.GUESTS ||
+                participantDisplayItem.getActorType() == Participant.ActorType.EMAILS) {
+                ImageViewExtensionsKt.loadFirstLetterAvatar(
+                    imageView,
+                    String.valueOf(participantDisplayItem.getNick().charAt(0))
+                );
+            } else {
+                ImageViewExtensionsKt.loadAvatarWithUrl(imageView,null, participantDisplayItem.getUrlForAvatar());
+            }
         }
 
         ImageView audioOffView = convertView.findViewById(R.id.remote_audio_off);

+ 5 - 4
app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt

@@ -17,6 +17,7 @@ import com.nextcloud.talk.R
 import com.nextcloud.talk.adapters.items.ParticipantItem.ParticipantItemViewHolder
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
+import com.nextcloud.talk.extensions.loadGuestAvatar
 import com.nextcloud.talk.extensions.loadUserAvatar
 import com.nextcloud.talk.models.json.mention.Mention
 import com.nextcloud.talk.models.json.status.StatusType
@@ -109,7 +110,6 @@ class MentionAutocompleteItem(
             )
         } else {
             holder.binding.nameText.text = displayName
-            holder.binding.secondaryText.text = "@$objectId"
         }
         var avatarId = objectId
         when (source) {
@@ -147,9 +147,9 @@ class MentionAutocompleteItem(
                 )
             }
 
-            SOURCE_GUESTS -> {
-                run { avatarId = displayName }
-                run { holder.binding.avatarView.loadUserAvatar(currentUser, avatarId!!, true, false) }
+            SOURCE_GUESTS, SOURCE_EMAILS -> {
+                avatarId = displayName
+                holder.binding.avatarView.loadGuestAvatar(currentUser, avatarId!!, false)
             }
 
             else -> {
@@ -218,6 +218,7 @@ class MentionAutocompleteItem(
         const val SOURCE_CALLS = "calls"
         const val SOURCE_GUESTS = "guests"
         const val SOURCE_GROUPS = "groups"
+        const val SOURCE_EMAILS = "emails"
         const val SOURCE_FEDERATION = "federated_users"
     }
 }

+ 21 - 14
app/src/main/java/com/nextcloud/talk/adapters/items/ParticipantItem.kt

@@ -21,16 +21,18 @@ import com.nextcloud.talk.adapters.items.ParticipantItem.ParticipantItemViewHold
 import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.databinding.RvItemConversationInfoParticipantBinding
+import com.nextcloud.talk.extensions.loadDefaultAvatar
 import com.nextcloud.talk.extensions.loadDefaultGroupCallAvatar
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
-import com.nextcloud.talk.extensions.loadGuestAvatar
-import com.nextcloud.talk.extensions.loadMailAvatar
+import com.nextcloud.talk.extensions.loadFirstLetterAvatar
 import com.nextcloud.talk.extensions.loadUserAvatar
+import com.nextcloud.talk.models.domain.ConversationModel
 import com.nextcloud.talk.models.json.participants.Participant
 import com.nextcloud.talk.models.json.participants.Participant.InCallFlags
 import com.nextcloud.talk.models.json.status.StatusType
 import com.nextcloud.talk.ui.StatusDrawable
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.talk.utils.ConversationUtils
 import com.nextcloud.talk.utils.DisplayUtils
 import com.nextcloud.talk.utils.DisplayUtils.convertDpToPixel
 import eu.davidea.flexibleadapter.FlexibleAdapter
@@ -45,7 +47,7 @@ class ParticipantItem(
     val model: Participant,
     private val user: User,
     private val viewThemeUtils: ViewThemeUtils,
-    private val roomToken: String
+    private val conversation: ConversationModel
 ) : AbstractFlexibleItem<ParticipantItemViewHolder>(), IFilterable<String?> {
     var isOnline = true
     override fun equals(o: Any?): Boolean {
@@ -128,7 +130,13 @@ class ParticipantItem(
             Participant.ParticipantType.GUEST -> {
                 userType = sharedApplication!!.getString(R.string.nc_guest)
                 if (model.calculatedActorType == Participant.ActorType.EMAILS) {
-                    userType = sharedApplication!!.getString(R.string.nc_email)
+                    userType = sharedApplication!!.getString(R.string.nc_guest)
+                }
+
+                if (model.invitedActorId?.isNotEmpty() == true &&
+                    ConversationUtils.isParticipantOwnerOrModerator(conversation)) {
+                    holder.binding.conversationInfoStatusMessage.text = model.invitedActorId
+                    alignUsernameVertical(holder, 0f)
                 }
             }
 
@@ -165,6 +173,7 @@ class ParticipantItem(
         }
     }
 
+    @SuppressLint("StringFormatInvalid")
     private fun showCallIcons(holder: ParticipantItemViewHolder) {
         val resources = sharedApplication!!.resources
         val inCallFlag = model.inCall
@@ -197,20 +206,18 @@ class ParticipantItem(
                 holder.binding.avatarView.loadDefaultGroupCallAvatar(viewThemeUtils)
             }
 
-            Participant.ActorType.EMAILS -> {
-                holder.binding.avatarView.loadMailAvatar(viewThemeUtils)
-            }
-
             Participant.ActorType.USERS -> {
                 holder.binding.avatarView.loadUserAvatar(user, model.calculatedActorId!!, true, false)
             }
 
-            Participant.ActorType.GUESTS -> {
-                var displayName: String? = sharedApplication!!.resources.getString(R.string.nc_guest)
-                if (!TextUtils.isEmpty(model.displayName)) {
-                    displayName = model.displayName
+            Participant.ActorType.GUESTS, Participant.ActorType.EMAILS -> {
+                if (model.displayName.isNullOrEmpty()) {
+                    holder.binding.avatarView.loadDefaultAvatar(viewThemeUtils)
+                } else {
+                    holder.binding.avatarView.loadFirstLetterAvatar(
+                        model.displayName!!.first().toString()
+                    )
                 }
-                holder.binding.avatarView.loadGuestAvatar(user, displayName!!, false)
             }
 
             Participant.ActorType.FEDERATED -> {
@@ -218,7 +225,7 @@ class ParticipantItem(
                 holder.binding.avatarView.loadFederatedUserAvatar(
                     user,
                     user.baseUrl!!,
-                    roomToken,
+                    conversation.token,
                     model.actorId!!,
                     darkTheme,
                     true,

+ 2 - 14
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingDeckCardViewHolder.kt

@@ -31,6 +31,7 @@ import com.nextcloud.talk.extensions.loadChangelogBotAvatar
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ChatMessageUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -168,7 +169,7 @@ class IncomingDeckCardViewHolder(incomingView: View, payload: Any) : MessageHold
         }
 
         if (!message.isGrouped && !message.isOneToOneConversation && !message.isFormerOneToOneConversation) {
-            setAvatarOnMessage(message)
+            ChatMessageUtils().setAvatarOnMessage(binding.messageUserAvatar, message, viewThemeUtils)
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {
                 binding.messageUserAvatar.visibility = View.GONE
@@ -179,19 +180,6 @@ class IncomingDeckCardViewHolder(incomingView: View, payload: Any) : MessageHold
         }
     }
 
-    private fun setAvatarOnMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        } else if (message.actorType == "federated_users") {
-            binding.messageUserAvatar.loadFederatedUserAvatar(message)
-        }
-    }
-
     private fun colorizeMessageBubble(message: ChatMessage) {
         viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
     }

+ 2 - 14
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt

@@ -28,6 +28,7 @@ import com.nextcloud.talk.extensions.loadChangelogBotAvatar
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ChatMessageUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -146,7 +147,7 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
         }
 
         if (!message.isGrouped && !message.isOneToOneConversation && !message.isFormerOneToOneConversation) {
-            setAvatarOnMessage(message)
+            ChatMessageUtils().setAvatarOnMessage(binding.messageUserAvatar, message, viewThemeUtils)
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {
                 binding.messageUserAvatar.visibility = View.GONE
@@ -157,19 +158,6 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
         }
     }
 
-    private fun setAvatarOnMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        } else if (message.actorType == "federated_users") {
-            binding.messageUserAvatar.loadFederatedUserAvatar(message)
-        }
-    }
-
     private fun colorizeMessageBubble(message: ChatMessage) {
         viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
     }

+ 2 - 14
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt

@@ -28,6 +28,7 @@ import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.polls.ui.PollMainDialogFragment
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ChatMessageUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -153,7 +154,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
         }
 
         if (!message.isGrouped && !message.isOneToOneConversation && !message.isFormerOneToOneConversation) {
-            setAvatarOnMessage(message)
+            ChatMessageUtils().setAvatarOnMessage(binding.messageUserAvatar, message, viewThemeUtils)
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {
                 binding.messageUserAvatar.visibility = View.GONE
@@ -164,19 +165,6 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
         }
     }
 
-    private fun setAvatarOnMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        } else if (message.actorType == "federated_users") {
-            binding.messageUserAvatar.loadFederatedUserAvatar(message)
-        }
-    }
-
     private fun colorizeMessageBubble(message: ChatMessage) {
         viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
     }

+ 4 - 25
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt

@@ -26,9 +26,12 @@ import com.nextcloud.talk.chat.data.model.ChatMessage
 import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
 import com.nextcloud.talk.extensions.loadBotsAvatar
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadDefaultAvatar
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
+import com.nextcloud.talk.extensions.loadFirstLetterAvatar
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ChatMessageUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.TextMatchers
 import com.nextcloud.talk.utils.message.MessageUtils
@@ -153,7 +156,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
         }
 
         if (!message.isGrouped && !message.isOneToOneConversation && !message.isFormerOneToOneConversation) {
-            setAvatarOnMessage(message)
+            ChatMessageUtils().setAvatarOnMessage(binding.messageUserAvatar, message, viewThemeUtils)
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {
                 binding.messageUserAvatar.visibility = View.GONE
@@ -164,19 +167,6 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
         }
     }
 
-    private fun setAvatarOnMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        } else if (message.actorType == "federated_users") {
-            binding.messageUserAvatar.loadFederatedUserAvatar(message)
-        }
-    }
-
     private fun colorizeMessageBubble(message: ChatMessage) {
         viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
     }
@@ -252,17 +242,6 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
         }
     }
 
-    private fun showAvatarOnChatMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        }
-    }
-
     fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
         this.commonMessageInterface = commonMessageInterface
     }

+ 2 - 14
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt

@@ -32,6 +32,7 @@ import com.nextcloud.talk.extensions.loadChangelogBotAvatar
 import com.nextcloud.talk.extensions.loadFederatedUserAvatar
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ChatMessageUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -249,7 +250,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
         }
 
         if (!message.isGrouped && !message.isOneToOneConversation && !message.isFormerOneToOneConversation) {
-            setAvatarOnMessage(message)
+            ChatMessageUtils().setAvatarOnMessage(binding.messageUserAvatar, message, viewThemeUtils)
         } else {
             if (message.isOneToOneConversation || message.isFormerOneToOneConversation) {
                 binding.messageUserAvatar.visibility = View.GONE
@@ -260,19 +261,6 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
         }
     }
 
-    private fun setAvatarOnMessage(message: ChatMessage) {
-        binding.messageUserAvatar.visibility = View.VISIBLE
-        if (message.actorType == "guests") {
-            // do nothing, avatar is set
-        } else if (message.actorType == "bots" && message.actorId == "changelog") {
-            binding.messageUserAvatar.loadChangelogBotAvatar()
-        } else if (message.actorType == "bots") {
-            binding.messageUserAvatar.loadBotsAvatar()
-        } else if (message.actorType == "federated_users") {
-            binding.messageUserAvatar.loadFederatedUserAvatar(message)
-        }
-    }
-
     private fun colorizeMessageBubble(message: ChatMessage) {
         viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
     }

+ 2 - 1
app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt

@@ -856,7 +856,8 @@ class MessageInputFragment : Fragment() {
                 val shouldQuote = mentionId.contains(" ") ||
                     mentionId.contains("@") ||
                     mentionId.startsWith("guest/") ||
-                    mentionId.startsWith("group/")
+                    mentionId.startsWith("group/") ||
+                    mentionId.startsWith("email/")
                 if (shouldQuote) {
                     mentionId = "\"" + mentionId + "\""
                 }

+ 1 - 1
app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt

@@ -568,7 +568,7 @@ class ConversationInfoActivity :
 
         for (i in participants.indices) {
             participant = participants[i]
-            userItem = ParticipantItem(this, participant, conversationUser, viewThemeUtils, conversationToken)
+            userItem = ParticipantItem(this, participant, conversationUser, viewThemeUtils, conversation!!)
             if (participant.sessionId != null) {
                 userItem.isOnline = !participant.sessionId.equals("0")
             } else {

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

@@ -10,12 +10,19 @@
 
 package com.nextcloud.talk.extensions
 
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.ColorDrawable
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.util.Log
 import android.widget.ImageView
 import androidx.core.content.ContextCompat
+import androidx.core.content.res.ResourcesCompat
 import coil.annotation.ExperimentalCoilApi
 import coil.imageLoader
 import coil.load
@@ -35,6 +42,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DisplayUtils
 import com.nextcloud.talk.utils.TextDrawable
+import java.util.Locale
 
 private const val ROUNDING_PIXEL = 16f
 private const val TAG = "ImageViewExtensions"
@@ -297,6 +305,21 @@ fun ImageView.loadNoteToSelfAvatar(): io.reactivex.disposables.Disposable {
     )
 }
 
+fun ImageView.loadFirstLetterAvatar(letter: String): io.reactivex.disposables.Disposable {
+    val layers = arrayOfNulls<Drawable>(2)
+    layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
+    layers[1] = createTextDrawable(context, letter.uppercase(Locale.ROOT))
+
+    val layerDrawable = LayerDrawable(layers)
+    val data: Any = layerDrawable
+
+    return DisposableWrapper(
+        load(data) {
+            transformations(CircleCropTransformation())
+        }
+    )
+}
+
 fun ImageView.loadChangelogBotAvatar(): io.reactivex.disposables.Disposable {
     return loadSystemAvatar()
 }
@@ -320,6 +343,11 @@ fun ImageView.loadDefaultGroupCallAvatar(viewThemeUtils: ViewThemeUtils): io.rea
     return loadUserAvatar(data)
 }
 
+fun ImageView.loadDefaultAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable {
+    val data: Any = viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.account_circle_96dp) as Any
+    return loadUserAvatar(data)
+}
+
 fun ImageView.loadDefaultPublicCallAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable {
     val data: Any = viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_link) as Any
     return loadUserAvatar(data)
@@ -350,6 +378,32 @@ fun ImageView.loadGuestAvatar(baseUrl: String, name: String, big: Boolean): io.r
     )
 }
 
+@Suppress("MagicNumber")
+private fun createTextDrawable(context: Context, letter: String): Drawable {
+    val size = 100
+    val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
+    val canvas = Canvas(bitmap)
+
+    val paint = Paint().apply {
+        color = ResourcesCompat.getColor(context.resources, R.color.grey_600, null)
+        style = Paint.Style.FILL
+    }
+    canvas.drawRect(0f, 0f, size.toFloat(), size.toFloat(), paint)
+
+    val textPaint = Paint().apply {
+        color = Color.WHITE
+        textSize = size / 2f
+        isAntiAlias = true
+        textAlign = Paint.Align.CENTER
+    }
+
+    val xPos = size / 2f
+    val yPos = (canvas.height / 2 - (textPaint.descent() + textPaint.ascent()) / 2)
+    canvas.drawText(letter.take(1), xPos, yPos, textPaint)
+
+    return BitmapDrawable(context.resources, bitmap)
+}
+
 private class DisposableWrapper(private val disposable: coil.request.Disposable) : io.reactivex.disposables
     .Disposable {
 

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

@@ -29,7 +29,7 @@ class ChatUtils {
 
                 if (individualHashMap != null) {
                     val type = individualHashMap["type"]
-                    resultMessage = if (type == "user" || type == "guest" || type == "call") {
+                    resultMessage = if (type == "user" || type == "guest" || type == "call" || type == "email") {
                         resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
                     } else if (type == "geo-location") {
                         individualHashMap["name"]

+ 3 - 0
app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt

@@ -72,6 +72,9 @@ data class Participant(
     @JsonField(name = ["statusMessage"])
     var statusMessage: String? = null,
 
+    @JsonField(name = ["invitedActorId"])
+    var invitedActorId: String? = null,
+
     var selected: Boolean = false
 ) : Parcelable {
     // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'

+ 37 - 0
app/src/main/java/com/nextcloud/talk/utils/ChatMessageUtils.kt

@@ -0,0 +1,37 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Marcel Hibbe <dev@mhibbe.de>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+package com.nextcloud.talk.utils
+
+import android.view.View
+import android.widget.ImageView
+import com.nextcloud.talk.chat.data.model.ChatMessage
+import com.nextcloud.talk.extensions.loadBotsAvatar
+import com.nextcloud.talk.extensions.loadChangelogBotAvatar
+import com.nextcloud.talk.extensions.loadDefaultAvatar
+import com.nextcloud.talk.extensions.loadFederatedUserAvatar
+import com.nextcloud.talk.extensions.loadFirstLetterAvatar
+import com.nextcloud.talk.ui.theme.ViewThemeUtils
+
+class ChatMessageUtils {
+
+    fun setAvatarOnMessage(view: ImageView, message: ChatMessage, viewThemeUtils : ViewThemeUtils) {
+        view.visibility = View.VISIBLE
+        if (message.actorType == "guests" || message.actorType == "emails") {
+            if (message.actorDisplayName?.isNotEmpty() == true) {
+                view.loadFirstLetterAvatar(message.actorDisplayName?.first().toString())
+            } else {
+                view.loadDefaultAvatar(viewThemeUtils)
+            }
+        } else if (message.actorType == "bots" && message.actorId == "changelog") {
+            view.loadChangelogBotAvatar()
+        } else if (message.actorType == "bots") {
+            view.loadBotsAvatar()
+        } else if (message.actorType == "federated_users") {
+            view.loadFederatedUserAvatar(message)
+        }
+    }
+}

+ 1 - 1
app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt

@@ -176,7 +176,7 @@ object DisplayUtils {
         chip.setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight)
         if (!isCallOrGroup) {
             var url = getUrlForAvatar(conversationUser.baseUrl, id, false)
-            if ("guests" == type || "guest" == type) {
+            if ("guests" == type || "guest" == type || "email" == type) {
                 url = getUrlForGuestAvatar(
                     conversationUser.baseUrl, label.toString(), true
                 )

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

@@ -107,7 +107,7 @@ class MessageUtils(val context: Context) {
             val individualHashMap = message.messageParameters!![key]
             if (individualHashMap != null) {
                 when (individualHashMap["type"]) {
-                    "user", "guest", "call", "user-group" -> {
+                    "user", "guest", "call", "user-group", "email" -> {
                         val chip = if (individualHashMap["id"] == message.activeUser!!.userId) {
                             R.xml.chip_you
                         } else {

+ 0 - 1
app/src/main/res/layout/rv_item_conversation_info_participant.xml

@@ -66,7 +66,6 @@
         android:ellipsize="end"
         android:maxLines="3"
         android:textAlignment="viewStart"
-        android:textAppearance="?android:attr/textAppearanceListItem"
         android:textColor="?android:attr/textColorSecondary"
         android:layout_marginEnd="@dimen/side_margin"
         app:layout_constraintBottom_toBottomOf="parent"