Browse Source

Fix removing an attendee from a conversation

Signed-off-by: Joas Schilling <coding@schilljs.com>
Joas Schilling 4 years ago
parent
commit
6f48ff9070

+ 36 - 29
app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java

@@ -21,7 +21,6 @@
 package com.nextcloud.talk.adapters.items;
 
 import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.ImageView;
@@ -150,37 +149,38 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
             holder.contactDisplayName.setText(NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest));
         }
 
-        if (TextUtils.isEmpty(participant.getSource()) || participant.getSource().equals("users")) {
-            if (Participant.ParticipantType.GUEST.equals(participant.getType()) ||
-                    Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participant.getType())) {
-                String displayName = NextcloudTalkApplication.Companion.getSharedApplication()
-                        .getResources().getString(R.string.nc_guest);
-
-                if (!TextUtils.isEmpty(participant.getDisplayName())) {
-                    displayName = participant.getDisplayName();
-                }
-
-                DraweeController draweeController = Fresco.newDraweeControllerBuilder()
-                        .setOldController(holder.simpleDraweeView.getController())
-                        .setAutoPlayAnimations(true)
-                        .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(),
-                                displayName, R.dimen.avatar_size), null))
-                        .build();
-                holder.simpleDraweeView.setController(draweeController);
-
-            } else {
+        if (participant.getActorType() == Participant.ActorType.GROUPS || "groups".equals(participant.getSource())) {
+            holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group);
+        } else if (participant.getActorType() == Participant.ActorType.EMAILS) {
+            // FIXME use an email icon
+            holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group);
+        } else if (participant.getActorType() == Participant.ActorType.GUESTS ||
+                Participant.ParticipantType.GUEST.equals(participant.getType()) ||
+                Participant.ParticipantType.GUEST_MODERATOR.equals(participant.getType())) {
 
-                DraweeController draweeController = Fresco.newDraweeControllerBuilder()
-                        .setOldController(holder.simpleDraweeView.getController())
-                        .setAutoPlayAnimations(true)
-                        .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
-                                participant.getUserId(), R.dimen.avatar_size), null))
-                        .build();
-                holder.simpleDraweeView.setController(draweeController);
+            String displayName = NextcloudTalkApplication.Companion.getSharedApplication()
+                    .getResources().getString(R.string.nc_guest);
 
+            if (!TextUtils.isEmpty(participant.getDisplayName())) {
+                displayName = participant.getDisplayName();
             }
-        } else if ("groups".equals(participant.getSource())) {
-            holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group);
+
+            DraweeController draweeController = Fresco.newDraweeControllerBuilder()
+                    .setOldController(holder.simpleDraweeView.getController())
+                    .setAutoPlayAnimations(true)
+                    .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(),
+                                                                                                                  displayName, R.dimen.avatar_size), null))
+                    .build();
+            holder.simpleDraweeView.setController(draweeController);
+
+        } else if (participant.getActorType() == Participant.ActorType.USERS || participant.getSource().equals("users")) {
+            DraweeController draweeController = Fresco.newDraweeControllerBuilder()
+                    .setOldController(holder.simpleDraweeView.getController())
+                    .setAutoPlayAnimations(true)
+                    .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
+                                                                                                         participant.getUserId(), R.dimen.avatar_size), null))
+                    .build();
+            holder.simpleDraweeView.setController(draweeController);
         }
 
         Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources();
@@ -246,13 +246,20 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
                         //userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_owner);
                         //break;
                     case 2:
+                    case 6: // Guest moderator
                         userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_moderator);
                         break;
                     case 3:
                         userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_user);
+                        if (participant.getActorType() == Participant.ActorType.GROUPS) {
+                            userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_group);
+                        }
                         break;
                     case 4:
                         userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest);
+                        if (participant.getActorType() == Participant.ActorType.EMAILS) {
+                            userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_email);
+                        }
                         break;
                     case 5:
                         userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_following_link);

+ 4 - 0
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -135,9 +135,13 @@ public interface NcApi {
 
 
     // also used for removing a guest from a conversation
+    @Deprecated
     @DELETE
     Observable<GenericOverall> removeParticipantFromConversation(@Header("Authorization") String authorization, @Url String url, @Query("participant") String participantId);
 
+    @DELETE
+    Observable<GenericOverall> removeAttendeeFromConversation(@Header("Authorization") String authorization, @Url String url, @Query("attendeeId") Long attendeeId);
+
     @POST
     Observable<GenericOverall> promoteUserToModerator(@Header("Authorization") String authorization, @Url String url, @Query("participant") String participantId);
 

+ 83 - 29
app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt

@@ -20,11 +20,13 @@
 
 package com.nextcloud.talk.controllers
 
+import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.os.Bundle
 import android.text.TextUtils
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.MenuItem
 import android.view.View
@@ -63,6 +65,8 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter
 import com.nextcloud.talk.models.json.generic.GenericOverall
 import com.nextcloud.talk.models.json.participants.Participant
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.GROUPS
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS
 import com.nextcloud.talk.models.json.participants.ParticipantsOverall
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
@@ -85,11 +89,13 @@ import io.reactivex.schedulers.Schedulers
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
+import retrofit2.adapter.rxjava2.HttpException
 import java.util.Calendar
 import java.util.Collections
 import java.util.Comparator
 import java.util.Locale
 import javax.inject.Inject
+import kotlin.reflect.typeOf
 
 @AutoInjector(NextcloudTalkApplication::class)
 class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener {
@@ -410,11 +416,20 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
         for (i in participants.indices) {
             participant = participants[i]
             userItem = UserItem(participant, conversationUser, null)
-            userItem.isOnline = !participant.sessionId.equals("0")
+            if (participant.sessionId != null) {
+                userItem.isOnline = !participant.sessionId.equals("0")
+            } else {
+                userItem.isOnline = !participant.sessionIds!!.isEmpty()
+            }
             if (!TextUtils.isEmpty(participant.userId) && participant.userId == conversationUser!!.userId) {
                 ownUserItem = userItem
                 ownUserItem.model.sessionId = "-1"
                 ownUserItem.isOnline = true
+            } else if (participant.actorType != null && participant.actorType == USERS
+                && !TextUtils.isEmpty(participant.actorId) && participant.actorId == conversationUser!!.userId) {
+                ownUserItem = userItem
+                ownUserItem.model.sessionId = "-1"
+                ownUserItem.isOnline = true
             } else {
                 recyclerViewItems.add(userItem)
             }
@@ -444,7 +459,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
         var apiVersion = 1
         // FIXME Fix API checking with guests?
         if (conversationUser != null) {
-            apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(1))
+            apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
         }
 
         ncApi.getPeersForCall(
@@ -716,7 +731,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
                     title(text = participant.displayName)
                     listItemsWithImage(items = items) { dialog, index, _ ->
 
-                        val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(1))
+                        val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
 
                         if (index == 0) {
                             if (participant.type == Participant.ParticipantType.MODERATOR) {
@@ -751,39 +766,69 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
                                     }
                             }
                         } else if (index == 1) {
-                            if (participant.type == Participant.ParticipantType.GUEST ||
-                                participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
-                            ) {
-                                ncApi.removeParticipantFromConversation(
+                            if (apiVersion >= ApiUtils.APIv4) {
+                                ncApi.removeAttendeeFromConversation(
                                     credentials,
-                                    ApiUtils.getUrlForRemovingParticipantFromConversation(
+                                    ApiUtils.getUrlForAttendees(
+                                        apiVersion,
                                         conversationUser.baseUrl,
-                                        conversation!!.token,
-                                        true
+                                        conversation!!.token
                                     ),
-                                    participant.sessionId
+                                    participant.attendeeId
                                 )
                                     .subscribeOn(Schedulers.io())
                                     .observeOn(AndroidSchedulers.mainThread())
-                                    .subscribe {
-                                        getListOfParticipants()
-                                    }
+                                    .subscribe(object : Observer<GenericOverall> {
+                                        override fun onSubscribe(d: Disposable) {
+                                        }
+
+                                        override fun onNext(genericOverall: GenericOverall) {
+                                            getListOfParticipants()
+                                        }
+
+                                        @SuppressLint("LongLogTag")
+                                        override fun onError(e: Throwable) {
+                                            Log.e(TAG, "Error removing attendee from conversation", e)
+                                        }
+
+                                        override fun onComplete() {
+                                        }
+                                    })
                             } else {
-                                ncApi.removeParticipantFromConversation(
-                                    credentials,
-                                    ApiUtils.getUrlForRemovingParticipantFromConversation(
-                                        conversationUser.baseUrl,
-                                        conversation!!.token,
-                                        false
-                                    ),
-                                    participant.userId
-                                )
-                                    .subscribeOn(Schedulers.io())
-                                    .observeOn(AndroidSchedulers.mainThread())
-                                    .subscribe {
-                                        getListOfParticipants()
-                                        // get participants again
-                                    }
+                                if (participant.type == Participant.ParticipantType.GUEST ||
+                                    participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
+                                ) {
+                                    ncApi.removeParticipantFromConversation(
+                                        credentials,
+                                        ApiUtils.getUrlForRemovingParticipantFromConversation(
+                                            conversationUser.baseUrl,
+                                            conversation!!.token,
+                                            true
+                                        ),
+                                        participant.sessionId
+                                    )
+                                        .subscribeOn(Schedulers.io())
+                                        .observeOn(AndroidSchedulers.mainThread())
+                                        .subscribe {
+                                            getListOfParticipants()
+                                        }
+                                } else {
+                                    ncApi.removeParticipantFromConversation(
+                                        credentials,
+                                        ApiUtils.getUrlForRemovingParticipantFromConversation(
+                                            conversationUser.baseUrl,
+                                            conversation!!.token,
+                                            false
+                                        ),
+                                        participant.userId
+                                    )
+                                        .subscribeOn(Schedulers.io())
+                                        .observeOn(AndroidSchedulers.mainThread())
+                                        .subscribe {
+                                            getListOfParticipants()
+                                            // get participants again
+                                        }
+                                }
                             }
                         }
                     }
@@ -796,6 +841,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
 
     companion object {
 
+        private const val TAG = "ConversationInfoController"
         private const val ID_DELETE_CONVERSATION_DIALOG = 0
     }
 
@@ -804,6 +850,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
      */
     class UserItemComparator : Comparator<UserItem> {
         override fun compare(left: UserItem, right: UserItem): Int {
+            val leftIsGroup = left.model.actorType == GROUPS
+            val rightIsGroup = right.model.actorType == GROUPS
+            if (leftIsGroup != rightIsGroup) {
+                // Groups below participants
+                return if (rightIsGroup) { -1 } else { 1 }
+            }
+
             if (left.isOnline && !right.isOnline) {
                 return -1
             } else if (!left.isOnline && right.isOnline) {
@@ -815,6 +868,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
             moderatorTypes.add(Participant.ParticipantType.OWNER)
             moderatorTypes.add(Participant.ParticipantType.GUEST_MODERATOR)
 
+
             if (moderatorTypes.contains(left.model.type) && !moderatorTypes.contains(right.model.type)) {
                 return -1
             } else if (!moderatorTypes.contains(left.model.type) && moderatorTypes.contains(right.model.type)) {

+ 56 - 0
app/src/main/java/com/nextcloud/talk/models/json/converters/EnumActorTypeConverter.kt

@@ -0,0 +1,56 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Joas Schilling
+ * Copyright (C) 2021 Joas Schilling <coding@schilljs.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.models.json.converters
+
+import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter
+import com.nextcloud.talk.models.json.participants.Participant
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.DUMMY
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.EMAILS
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.GROUPS
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.GUESTS
+import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS
+
+class EnumActorTypeConverter : StringBasedTypeConverter<Participant.ActorType>() {
+    override fun getFromString(string: String): Participant.ActorType {
+        when (string) {
+            "emails" -> return EMAILS
+            "groups" -> return GROUPS
+            "guests" -> return GUESTS
+            "users" -> return USERS
+            else -> return DUMMY
+        }
+    }
+
+    override fun convertToString(`object`: Participant.ActorType?): String {
+
+        if (`object` == null) {
+            return ""
+        }
+
+        when (`object`) {
+            EMAILS -> return "emails"
+            GROUPS -> return "groups"
+            GUESTS -> return "guests"
+            USERS -> return "users"
+            else -> return ""
+        }
+    }
+}

+ 139 - 52
app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java

@@ -22,21 +22,36 @@ package com.nextcloud.talk.models.json.participants;
 
 import com.bluelinelabs.logansquare.annotation.JsonField;
 import com.bluelinelabs.logansquare.annotation.JsonObject;
+import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter;
 import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
+import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter;
 import com.nextcloud.talk.models.json.converters.ObjectParcelConverter;
 
 import org.parceler.Parcel;
 import org.parceler.ParcelPropertyConverter;
 
+import java.util.Arrays;
+
 @Parcel
 @JsonObject
 public class Participant {
+    @JsonField(name = "attendeeId")
+    public Long attendeeId;
+
+    @JsonField(name = "actorType", typeConverter = EnumActorTypeConverter.class)
+    public ActorType actorType;
+
+    @JsonField(name = "actorId")
+    public String actorId;
+
+    @Deprecated
     @JsonField(name = "userId")
     public String userId;
 
     @JsonField(name = {"type", "participantType"}, typeConverter = EnumParticipantTypeConverter.class)
     public ParticipantType type;
 
+    @Deprecated
     @JsonField(name = "name")
     public String name;
 
@@ -46,15 +61,21 @@ public class Participant {
     @JsonField(name = "lastPing")
     public long lastPing;
 
+    @Deprecated
     @JsonField(name = "sessionId")
     public String sessionId;
 
+    @JsonField(name = "sessionIds")
+    public String[] sessionIds;
+
+    @Deprecated
     @JsonField(name = "roomId")
     public long roomId;
 
     @ParcelPropertyConverter(ObjectParcelConverter.class)
     @JsonField(name = "inCall")
     public Object inCall;
+
     public String source;
 
     public boolean selected;
@@ -76,7 +97,30 @@ public class Participant {
         return participantFlags;
     }
 
+    public Long getAttendeeId() {
+        return attendeeId;
+    }
+
+    public ActorType getActorType() {
+        if (this.userId != null) {
+            return ActorType.USERS;
+        }
+        return actorType;
+    }
+
+    public String getActorId() {
+        if (this.userId != null) {
+            return this.userId;
+        }
+        return actorId;
+    }
+
+
+    @Deprecated
     public String getUserId() {
+        if (this.actorType != null && this.actorType == ActorType.USERS) {
+            return this.actorId;
+        }
         return this.userId;
     }
 
@@ -84,6 +128,7 @@ public class Participant {
         return this.type;
     }
 
+    @Deprecated
     public String getName() {
         return this.name;
     }
@@ -96,10 +141,16 @@ public class Participant {
         return this.lastPing;
     }
 
+    @Deprecated
     public String getSessionId() {
         return this.sessionId;
     }
 
+    public String[] getSessionIds() {
+        return sessionIds;
+    }
+
+    @Deprecated
     public long getRoomId() {
         return this.roomId;
     }
@@ -116,14 +167,28 @@ public class Participant {
         return this.selected;
     }
 
+    @Deprecated
     public void setUserId(String userId) {
         this.userId = userId;
     }
 
+    public void setAttendeeId(Long attendeeId) {
+        this.attendeeId = attendeeId;
+    }
+
+    public void setActorType(ActorType actorType) {
+        this.actorType = actorType;
+    }
+
+    public void setActorId(String actorId) {
+        this.actorId = actorId;
+    }
+
     public void setType(ParticipantType type) {
         this.type = type;
     }
 
+    @Deprecated
     public void setName(String name) {
         this.name = name;
     }
@@ -136,10 +201,12 @@ public class Participant {
         this.lastPing = lastPing;
     }
 
+    @Deprecated
     public void setSessionId(String sessionId) {
         this.sessionId = sessionId;
     }
 
+    @Deprecated
     public void setRoomId(long roomId) {
         this.roomId = roomId;
     }
@@ -156,93 +223,113 @@ public class Participant {
         this.selected = selected;
     }
 
-    public boolean equals(final Object o) {
-        if (o == this) {
+    public void setSessionIds(String[] sessionIds) {
+        this.sessionIds = sessionIds;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
             return true;
         }
-        if (!(o instanceof Participant)) {
+        if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        final Participant other = (Participant) o;
-        if (!other.canEqual((Object) this)) {
+
+        Participant that = (Participant) o;
+
+        if (lastPing != that.lastPing) {
             return false;
         }
-        final Object this$userId = this.getUserId();
-        final Object other$userId = other.getUserId();
-        if (this$userId == null ? other$userId != null : !this$userId.equals(other$userId)) {
+        if (roomId != that.roomId) {
             return false;
         }
-        final Object this$type = this.getType();
-        final Object other$type = other.getType();
-        if (this$type == null ? other$type != null : !this$type.equals(other$type)) {
+        if (selected != that.selected) {
             return false;
         }
-        final Object this$name = this.getName();
-        final Object other$name = other.getName();
-        if (this$name == null ? other$name != null : !this$name.equals(other$name)) {
+        if (!attendeeId.equals(that.attendeeId)) {
             return false;
         }
-        final Object this$displayName = this.getDisplayName();
-        final Object other$displayName = other.getDisplayName();
-        if (this$displayName == null ? other$displayName != null : !this$displayName.equals(other$displayName)) {
+        if (!actorType.equals(that.actorType)) {
             return false;
         }
-        if (this.getLastPing() != other.getLastPing()) {
+        if (!actorId.equals(that.actorId)) {
             return false;
         }
-        final Object this$sessionId = this.getSessionId();
-        final Object other$sessionId = other.getSessionId();
-        if (this$sessionId == null ? other$sessionId != null : !this$sessionId.equals(other$sessionId)) {
+        if (!userId.equals(that.userId)) {
             return false;
         }
-        if (this.getRoomId() != other.getRoomId()) {
+        if (type != that.type) {
             return false;
         }
-        final Object this$inCall = this.getInCall();
-        final Object other$inCall = other.getInCall();
-        if (this$inCall == null ? other$inCall != null : !this$inCall.equals(other$inCall)) {
+        if (!name.equals(that.name)) {
             return false;
         }
-        final Object this$source = this.getSource();
-        final Object other$source = other.getSource();
-        if (this$source == null ? other$source != null : !this$source.equals(other$source)) {
+        if (displayName != null ? !displayName.equals(that.displayName) : that.displayName != null) {
             return false;
         }
-
-        return this.isSelected() == other.isSelected();
+        if (!sessionId.equals(that.sessionId)) {
+            return false;
+        }
+        // Probably incorrect - comparing Object[] arrays with Arrays.equals
+        if (!Arrays.equals(sessionIds, that.sessionIds)) {
+            return false;
+        }
+        if (inCall != null ? !inCall.equals(that.inCall) : that.inCall != null) {
+            return false;
+        }
+        return source != null ? source.equals(that.source) : that.source == null;
     }
 
     protected boolean canEqual(final Object other) {
         return other instanceof Participant;
     }
 
+    @Override
     public int hashCode() {
-        final int PRIME = 59;
-        int result = 1;
-        final Object $userId = this.getUserId();
-        result = result * PRIME + ($userId == null ? 43 : $userId.hashCode());
-        final Object $type = this.getType();
-        result = result * PRIME + ($type == null ? 43 : $type.hashCode());
-        final Object $name = this.getName();
-        result = result * PRIME + ($name == null ? 43 : $name.hashCode());
-        final Object $displayName = this.getDisplayName();
-        result = result * PRIME + ($displayName == null ? 43 : $displayName.hashCode());
-        final long $lastPing = this.getLastPing();
-        result = result * PRIME + (int) ($lastPing >>> 32 ^ $lastPing);
-        final Object $sessionId = this.getSessionId();
-        result = result * PRIME + ($sessionId == null ? 43 : $sessionId.hashCode());
-        final long $roomId = this.getRoomId();
-        result = result * PRIME + (int) ($roomId >>> 32 ^ $roomId);
-        final Object $inCall = this.getInCall();
-        result = result * PRIME + ($inCall == null ? 43 : $inCall.hashCode());
-        final Object $source = this.getSource();
-        result = result * PRIME + ($source == null ? 43 : $source.hashCode());
-        result = result * PRIME + (this.isSelected() ? 79 : 97);
+        int result = attendeeId.hashCode();
+        result = 31 * result + actorType.hashCode();
+        result = 31 * result + actorId.hashCode();
+        result = 31 * result + (userId != null ? userId.hashCode() : 0);
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
+        result = 31 * result + (int) (lastPing ^ (lastPing >>> 32));
+        result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0);
+        result = 31 * result + Arrays.hashCode(sessionIds);
+        result = 31 * result + (int) (roomId ^ (roomId >>> 32));
+        result = 31 * result + (inCall != null ? inCall.hashCode() : 0);
+        result = 31 * result + (source != null ? source.hashCode() : 0);
+        result = 31 * result + (selected ? 1 : 0);
         return result;
     }
 
+    @Override
     public String toString() {
-        return "Participant(userId=" + this.getUserId() + ", type=" + this.getType() + ", name=" + this.getName() + ", displayName=" + this.getDisplayName() + ", lastPing=" + this.getLastPing() + ", sessionId=" + this.getSessionId() + ", roomId=" + this.getRoomId() + ", inCall=" + this.getInCall() + ", source=" + this.getSource() + ", selected=" + this.isSelected() + ")";
+        return "Participant{" +
+                "attendeeId=" + attendeeId +
+                ", actorType='" + actorType + '\'' +
+                ", actorId='" + actorId + '\'' +
+                ", userId='" + userId + '\'' +
+                ", type=" + type +
+                ", name='" + name + '\'' +
+                ", displayName='" + displayName + '\'' +
+                ", lastPing=" + lastPing +
+                ", sessionId='" + sessionId + '\'' +
+                ", sessionIds=" + Arrays.toString(sessionIds) +
+                ", roomId=" + roomId +
+                ", inCall=" + inCall +
+                ", source='" + source + '\'' +
+                ", selected=" + selected +
+                '}';
+    }
+
+    public enum ActorType {
+        DUMMY,
+        EMAILS,
+        GROUPS,
+        GUESTS,
+        USERS,
     }
 
     public enum ParticipantType {

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -305,6 +305,8 @@
 
     <!-- Other -->
     <string name="nc_limit_hit">%s characters limit has been hit</string>
+    <string name="nc_email">Email</string>
+    <string name="nc_group">Group</string>
     <string name="nc_groups">Groups</string>
     <string name="nc_participants">Participants</string>
     <string name="nc_participants_add">Add participants</string>