1package eu.siacs.conversations.entities;
2
3import android.support.annotation.NonNull;
4import android.support.annotation.Nullable;
5import android.text.TextUtils;
6
7import java.util.ArrayList;
8import java.util.Collections;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Locale;
12import java.util.Set;
13
14import eu.siacs.conversations.Config;
15import eu.siacs.conversations.R;
16import eu.siacs.conversations.services.AvatarService;
17import eu.siacs.conversations.services.MessageArchiveService;
18import eu.siacs.conversations.utils.JidHelper;
19import eu.siacs.conversations.utils.UIHelper;
20import eu.siacs.conversations.xmpp.chatstate.ChatState;
21import eu.siacs.conversations.xmpp.forms.Data;
22import eu.siacs.conversations.xmpp.forms.Field;
23import eu.siacs.conversations.xmpp.pep.Avatar;
24import rocks.xmpp.addr.Jid;
25
26public class MucOptions {
27
28 public static final String STATUS_CODE_SELF_PRESENCE = "110";
29 public static final String STATUS_CODE_ROOM_CREATED = "201";
30 public static final String STATUS_CODE_BANNED = "301";
31 public static final String STATUS_CODE_CHANGED_NICK = "303";
32 public static final String STATUS_CODE_KICKED = "307";
33 public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
34 public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
35 public static final String STATUS_CODE_SHUTDOWN = "332";
36 private final Set<User> users = new HashSet<>();
37 private final Conversation conversation;
38 public OnRenameListener onRenameListener = null;
39 private boolean mAutoPushConfiguration = true;
40 private Account account;
41 private ServiceDiscoveryResult serviceDiscoveryResult;
42 private boolean isOnline = false;
43 private Error error = Error.NONE;
44 private User self;
45 private String password = null;
46
47 private boolean tookProposedNickFromBookmark = false;
48
49 public MucOptions(Conversation conversation) {
50 this.account = conversation.getAccount();
51 this.conversation = conversation;
52 this.self = new User(this, createJoinJid(getProposedNick()));
53 this.self.affiliation = Affiliation.of(conversation.getAttribute("affiliation"));
54 this.self.role = Role.of(conversation.getAttribute("role"));
55 }
56
57 public Account getAccount() {
58 return this.conversation.getAccount();
59 }
60
61 public boolean setSelf(User user) {
62 this.self = user;
63 final boolean roleChanged = this.conversation.setAttribute("role", user.role.toString());
64 final boolean affiliationChanged = this.conversation.setAttribute("affiliation", user.affiliation.toString());
65 return roleChanged || affiliationChanged;
66 }
67
68 public void changeAffiliation(Jid jid, Affiliation affiliation) {
69 User user = findUserByRealJid(jid);
70 synchronized (users) {
71 if (user != null && user.getRole() == Role.NONE) {
72 users.remove(user);
73 if (affiliation.ranks(Affiliation.MEMBER)) {
74 user.affiliation = affiliation;
75 users.add(user);
76 }
77 }
78 }
79 }
80
81 public void flagNoAutoPushConfiguration() {
82 mAutoPushConfiguration = false;
83 }
84
85 public boolean autoPushConfiguration() {
86 return mAutoPushConfiguration;
87 }
88
89 public boolean isSelf(Jid counterpart) {
90 return counterpart.equals(self.getFullJid());
91 }
92
93 public void resetChatState() {
94 synchronized (users) {
95 for (User user : users) {
96 user.chatState = Config.DEFAULT_CHATSTATE;
97 }
98 }
99 }
100
101 public boolean isTookProposedNickFromBookmark() {
102 return tookProposedNickFromBookmark;
103 }
104
105 void notifyOfBookmarkNick(final String nick) {
106 final String normalized = normalize(account.getJid(),nick);
107 if (normalized != null && normalized.equals(getSelf().getFullJid().getResource())) {
108 this.tookProposedNickFromBookmark = true;
109 }
110 }
111
112 public boolean mamSupport() {
113 return MessageArchiveService.Version.has(getFeatures());
114 }
115
116 public boolean updateConfiguration(ServiceDiscoveryResult serviceDiscoveryResult) {
117 this.serviceDiscoveryResult = serviceDiscoveryResult;
118 String name;
119 Field roomConfigName = getRoomInfoForm().getFieldByName("muc#roomconfig_roomname");
120 if (roomConfigName != null) {
121 name = roomConfigName.getValue();
122 } else {
123 List<ServiceDiscoveryResult.Identity> identities = serviceDiscoveryResult.getIdentities();
124 String identityName = identities.size() > 0 ? identities.get(0).getName() : null;
125 final Jid jid = conversation.getJid();
126 if (identityName != null && !identityName.equals(jid == null ? null : jid.getEscapedLocal())) {
127 name = identityName;
128 } else {
129 name = null;
130 }
131 }
132 boolean changed = conversation.setAttribute("muc_name", name);
133 changed |= conversation.setAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, this.hasFeature("muc_membersonly"));
134 changed |= conversation.setAttribute(Conversation.ATTRIBUTE_MODERATED, this.hasFeature("muc_moderated"));
135 changed |= conversation.setAttribute(Conversation.ATTRIBUTE_NON_ANONYMOUS, this.hasFeature("muc_nonanonymous"));
136 return changed;
137 }
138
139 private Data getRoomInfoForm() {
140 final List<Data> forms = serviceDiscoveryResult == null ? Collections.emptyList() : serviceDiscoveryResult.forms;
141 return forms.size() == 0 ? new Data() : forms.get(0);
142 }
143
144 public String getAvatar() {
145 return account.getRoster().getContact(conversation.getJid()).getAvatarFilename();
146 }
147
148 public boolean hasFeature(String feature) {
149 return this.serviceDiscoveryResult != null && this.serviceDiscoveryResult.features.contains(feature);
150 }
151
152 public boolean hasVCards() {
153 return hasFeature("vcard-temp");
154 }
155
156 public boolean canInvite() {
157 Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowinvites");
158 return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
159 }
160
161 public boolean canChangeSubject() {
162 Field field = getRoomInfoForm().getFieldByName("muc#roominfo_changesubject");
163 return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
164 }
165
166 public boolean allowPm() {
167 final Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowpm");
168 if (field == null) {
169 return true; //fall back if field does not exists
170 }
171 if ("anyone".equals(field.getValue())) {
172 return true;
173 } else if ("participants".equals(field.getValue())) {
174 return self.getRole().ranks(Role.PARTICIPANT);
175 } else if ("moderators".equals(field.getValue())) {
176 return self.getRole().ranks(Role.MODERATOR);
177 } else {
178 return false;
179 }
180 }
181
182 public boolean participating() {
183 return self.getRole().ranks(Role.PARTICIPANT) || !moderated();
184 }
185
186 public boolean membersOnly() {
187 return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, false);
188 }
189
190 public List<String> getFeatures() {
191 return this.serviceDiscoveryResult != null ? this.serviceDiscoveryResult.features : Collections.emptyList();
192 }
193
194 public boolean nonanonymous() {
195 return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_NON_ANONYMOUS, false);
196 }
197
198 public boolean isPrivateAndNonAnonymous() {
199 return membersOnly() && nonanonymous();
200 }
201
202 public boolean moderated() {
203 return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MODERATED, false);
204 }
205
206 public User deleteUser(Jid jid) {
207 User user = findUserByFullJid(jid);
208 if (user != null) {
209 synchronized (users) {
210 users.remove(user);
211 boolean realJidInMuc = false;
212 for (User u : users) {
213 if (user.realJid != null && user.realJid.equals(u.realJid)) {
214 realJidInMuc = true;
215 break;
216 }
217 }
218 boolean self = user.realJid != null && user.realJid.equals(account.getJid().asBareJid());
219 if (membersOnly()
220 && nonanonymous()
221 && user.affiliation.ranks(Affiliation.MEMBER)
222 && user.realJid != null
223 && !realJidInMuc
224 && !self) {
225 user.role = Role.NONE;
226 user.avatar = null;
227 user.fullJid = null;
228 users.add(user);
229 }
230 }
231 }
232 return user;
233 }
234
235 //returns true if real jid was new;
236 public boolean updateUser(User user) {
237 User old;
238 boolean realJidFound = false;
239 if (user.fullJid == null && user.realJid != null) {
240 old = findUserByRealJid(user.realJid);
241 realJidFound = old != null;
242 if (old != null) {
243 if (old.fullJid != null) {
244 return false; //don't add. user already exists
245 } else {
246 synchronized (users) {
247 users.remove(old);
248 }
249 }
250 }
251 } else if (user.realJid != null) {
252 old = findUserByRealJid(user.realJid);
253 realJidFound = old != null;
254 synchronized (users) {
255 if (old != null && (old.fullJid == null || old.role == Role.NONE)) {
256 users.remove(old);
257 }
258 }
259 }
260 old = findUserByFullJid(user.getFullJid());
261
262 synchronized (this.users) {
263 if (old != null) {
264 users.remove(old);
265 }
266 boolean fullJidIsSelf = isOnline && user.getFullJid() != null && user.getFullJid().equals(self.getFullJid());
267 if ((!membersOnly() || user.getAffiliation().ranks(Affiliation.MEMBER))
268 && user.getAffiliation().outranks(Affiliation.OUTCAST)
269 && !fullJidIsSelf) {
270 this.users.add(user);
271 return !realJidFound && user.realJid != null;
272 }
273 }
274 return false;
275 }
276
277 public User findUserByFullJid(Jid jid) {
278 if (jid == null) {
279 return null;
280 }
281 synchronized (users) {
282 for (User user : users) {
283 if (jid.equals(user.getFullJid())) {
284 return user;
285 }
286 }
287 }
288 return null;
289 }
290
291 public User findUserByRealJid(Jid jid) {
292 if (jid == null) {
293 return null;
294 }
295 synchronized (users) {
296 for (User user : users) {
297 if (jid.equals(user.realJid)) {
298 return user;
299 }
300 }
301 }
302 return null;
303 }
304
305 public User findOrCreateUserByRealJid(Jid jid, Jid fullJid) {
306 User user = findUserByRealJid(jid);
307 if (user == null) {
308 user = new User(this, fullJid);
309 user.setRealJid(jid);
310 }
311 return user;
312 }
313
314 public User findUser(ReadByMarker readByMarker) {
315 if (readByMarker.getRealJid() != null) {
316 return findOrCreateUserByRealJid(readByMarker.getRealJid().asBareJid(), readByMarker.getFullJid());
317 } else if (readByMarker.getFullJid() != null) {
318 return findUserByFullJid(readByMarker.getFullJid());
319 } else {
320 return null;
321 }
322 }
323
324 public boolean isContactInRoom(Contact contact) {
325 return findUserByRealJid(contact.getJid().asBareJid()) != null;
326 }
327
328 public boolean isUserInRoom(Jid jid) {
329 return findUserByFullJid(jid) != null;
330 }
331
332 public boolean setOnline() {
333 boolean before = this.isOnline;
334 this.isOnline = true;
335 return !before;
336 }
337
338 public ArrayList<User> getUsers() {
339 return getUsers(true);
340 }
341
342 public ArrayList<User> getUsers(boolean includeOffline) {
343 synchronized (users) {
344 ArrayList<User> users = new ArrayList<>();
345 for (User user : this.users) {
346 if (!user.isDomain() && (includeOffline || user.getRole().ranks(Role.PARTICIPANT))) {
347 users.add(user);
348 }
349 }
350 return users;
351 }
352 }
353
354 public ArrayList<User> getUsersWithChatState(ChatState state, int max) {
355 synchronized (users) {
356 ArrayList<User> list = new ArrayList<>();
357 for (User user : users) {
358 if (user.chatState == state) {
359 list.add(user);
360 if (list.size() >= max) {
361 break;
362 }
363 }
364 }
365 return list;
366 }
367 }
368
369 public List<User> getUsers(int max) {
370 ArrayList<User> subset = new ArrayList<>();
371 HashSet<Jid> jids = new HashSet<>();
372 jids.add(account.getJid().asBareJid());
373 synchronized (users) {
374 for (User user : users) {
375 if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
376 subset.add(user);
377 }
378 if (subset.size() >= max) {
379 break;
380 }
381 }
382 }
383 return subset;
384 }
385
386 public static List<User> sub(List<User> users, int max) {
387 ArrayList<User> subset = new ArrayList<>();
388 HashSet<Jid> jids = new HashSet<>();
389 for (User user : users) {
390 jids.add(user.getAccount().getJid().asBareJid());
391 if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
392 subset.add(user);
393 }
394 if (subset.size() >= max) {
395 break;
396 }
397 }
398 return subset;
399 }
400
401 public int getUserCount() {
402 synchronized (users) {
403 return users.size();
404 }
405 }
406
407 private String getProposedNick() {
408 final Bookmark bookmark = this.conversation.getBookmark();
409 final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
410 if (bookmarkedNick != null) {
411 this.tookProposedNickFromBookmark = true;
412 return bookmarkedNick;
413 } else if (!conversation.getJid().isBareJid()) {
414 return conversation.getJid().getResource();
415 } else {
416 final String displayName = normalize(account.getJid(), account.getDisplayName());
417 if (displayName == null) {
418 return JidHelper.localPartOrFallback(account.getJid());
419 } else {
420 return displayName;
421 }
422 }
423 }
424
425 private static String normalize(Jid account, String nick) {
426 if (account == null || TextUtils.isEmpty(nick)) {
427 return null;
428 }
429 try {
430 return account.withResource(nick).getResource();
431 } catch (IllegalArgumentException e) {
432 return null;
433 }
434
435 }
436
437 public String getActualNick() {
438 if (this.self.getName() != null) {
439 return this.self.getName();
440 } else {
441 return this.getProposedNick();
442 }
443 }
444
445 public boolean online() {
446 return this.isOnline;
447 }
448
449 public Error getError() {
450 return this.error;
451 }
452
453 public void setError(Error error) {
454 this.isOnline = isOnline && error == Error.NONE;
455 this.error = error;
456 }
457
458 public void setOnRenameListener(OnRenameListener listener) {
459 this.onRenameListener = listener;
460 }
461
462 public void setOffline() {
463 synchronized (users) {
464 this.users.clear();
465 }
466 this.error = Error.NO_RESPONSE;
467 this.isOnline = false;
468 }
469
470 public User getSelf() {
471 return self;
472 }
473
474 public boolean setSubject(String subject) {
475 return this.conversation.setAttribute("subject", subject);
476 }
477
478 public String getSubject() {
479 return this.conversation.getAttribute("subject");
480 }
481
482 public String getName() {
483 return this.conversation.getAttribute("muc_name");
484 }
485
486 private List<User> getFallbackUsersFromCryptoTargets() {
487 List<User> users = new ArrayList<>();
488 for (Jid jid : conversation.getAcceptedCryptoTargets()) {
489 User user = new User(this, null);
490 user.setRealJid(jid);
491 users.add(user);
492 }
493 return users;
494 }
495
496 public List<User> getUsersRelevantForNameAndAvatar() {
497 final List<User> users;
498 if (isOnline) {
499 users = getUsers(5);
500 } else {
501 users = getFallbackUsersFromCryptoTargets();
502 }
503 return users;
504 }
505
506 public String createNameFromParticipants() {
507 List<User> users = getUsersRelevantForNameAndAvatar();
508 if (users.size() >= 2) {
509 StringBuilder builder = new StringBuilder();
510 for (User user : users) {
511 if (builder.length() != 0) {
512 builder.append(", ");
513 }
514 String name = UIHelper.getDisplayName(user);
515 if (name != null) {
516 builder.append(name.split("\\s+")[0]);
517 }
518 }
519 return builder.toString();
520 } else {
521 return null;
522 }
523 }
524
525 public long[] getPgpKeyIds() {
526 List<Long> ids = new ArrayList<>();
527 for (User user : this.users) {
528 if (user.getPgpKeyId() != 0) {
529 ids.add(user.getPgpKeyId());
530 }
531 }
532 ids.add(account.getPgpId());
533 long[] primitiveLongArray = new long[ids.size()];
534 for (int i = 0; i < ids.size(); ++i) {
535 primitiveLongArray[i] = ids.get(i);
536 }
537 return primitiveLongArray;
538 }
539
540 public boolean pgpKeysInUse() {
541 synchronized (users) {
542 for (User user : users) {
543 if (user.getPgpKeyId() != 0) {
544 return true;
545 }
546 }
547 }
548 return false;
549 }
550
551 public boolean everybodyHasKeys() {
552 synchronized (users) {
553 for (User user : users) {
554 if (user.getPgpKeyId() == 0) {
555 return false;
556 }
557 }
558 }
559 return true;
560 }
561
562 public Jid createJoinJid(String nick) {
563 try {
564 return conversation.getJid().withResource(nick);
565 } catch (final IllegalArgumentException e) {
566 return null;
567 }
568 }
569
570 public Jid getTrueCounterpart(Jid jid) {
571 if (jid.equals(getSelf().getFullJid())) {
572 return account.getJid().asBareJid();
573 }
574 User user = findUserByFullJid(jid);
575 return user == null ? null : user.realJid;
576 }
577
578 public String getPassword() {
579 this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
580 if (this.password == null && conversation.getBookmark() != null
581 && conversation.getBookmark().getPassword() != null) {
582 return conversation.getBookmark().getPassword();
583 } else {
584 return this.password;
585 }
586 }
587
588 public void setPassword(String password) {
589 if (conversation.getBookmark() != null) {
590 conversation.getBookmark().setPassword(password);
591 } else {
592 this.password = password;
593 }
594 conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
595 }
596
597 public Conversation getConversation() {
598 return this.conversation;
599 }
600
601 public List<Jid> getMembers(final boolean includeDomains) {
602 ArrayList<Jid> members = new ArrayList<>();
603 synchronized (users) {
604 for (User user : users) {
605 if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null && !user.realJid.asBareJid().equals(conversation.account.getJid().asBareJid()) && (!user.isDomain() || includeDomains)) {
606 members.add(user.realJid);
607 }
608 }
609 }
610 return members;
611 }
612
613 public enum Affiliation {
614 OWNER(4, R.string.owner),
615 ADMIN(3, R.string.admin),
616 MEMBER(2, R.string.member),
617 OUTCAST(0, R.string.outcast),
618 NONE(1, R.string.no_affiliation);
619
620 private int resId;
621 private int rank;
622
623 Affiliation(int rank, int resId) {
624 this.resId = resId;
625 this.rank = rank;
626 }
627
628 public static Affiliation of(@Nullable String value) {
629 if (value == null) {
630 return NONE;
631 }
632 try {
633 return Affiliation.valueOf(value.toUpperCase(Locale.US));
634 } catch (IllegalArgumentException e) {
635 return NONE;
636 }
637 }
638
639 public int getResId() {
640 return resId;
641 }
642
643 @Override
644 public String toString() {
645 return name().toLowerCase(Locale.US);
646 }
647
648 public boolean outranks(Affiliation affiliation) {
649 return rank > affiliation.rank;
650 }
651
652 public boolean ranks(Affiliation affiliation) {
653 return rank >= affiliation.rank;
654 }
655 }
656
657 public enum Role {
658 MODERATOR(R.string.moderator, 3),
659 VISITOR(R.string.visitor, 1),
660 PARTICIPANT(R.string.participant, 2),
661 NONE(R.string.no_role, 0);
662
663 private int resId;
664 private int rank;
665
666 Role(int resId, int rank) {
667 this.resId = resId;
668 this.rank = rank;
669 }
670
671 public static Role of(@Nullable String value) {
672 if (value == null) {
673 return NONE;
674 }
675 try {
676 return Role.valueOf(value.toUpperCase(Locale.US));
677 } catch (IllegalArgumentException e) {
678 return NONE;
679 }
680 }
681
682 public int getResId() {
683 return resId;
684 }
685
686 @Override
687 public String toString() {
688 return name().toLowerCase(Locale.US);
689 }
690
691 public boolean ranks(Role role) {
692 return rank >= role.rank;
693 }
694 }
695
696 public enum Error {
697 NO_RESPONSE,
698 SERVER_NOT_FOUND,
699 REMOTE_SERVER_TIMEOUT,
700 NONE,
701 NICK_IN_USE,
702 PASSWORD_REQUIRED,
703 BANNED,
704 MEMBERS_ONLY,
705 RESOURCE_CONSTRAINT,
706 KICKED,
707 SHUTDOWN,
708 DESTROYED,
709 INVALID_NICK,
710 UNKNOWN,
711 NON_ANONYMOUS
712 }
713
714 private interface OnEventListener {
715 void onSuccess();
716
717 void onFailure();
718 }
719
720 public interface OnRenameListener extends OnEventListener {
721
722 }
723
724 public static class User implements Comparable<User>, AvatarService.Avatarable {
725 private Role role = Role.NONE;
726 private Affiliation affiliation = Affiliation.NONE;
727 private Jid realJid;
728 private Jid fullJid;
729 private long pgpKeyId = 0;
730 private Avatar avatar;
731 private MucOptions options;
732 private ChatState chatState = Config.DEFAULT_CHATSTATE;
733
734 public User(MucOptions options, Jid fullJid) {
735 this.options = options;
736 this.fullJid = fullJid;
737 }
738
739 public String getName() {
740 return fullJid == null ? null : fullJid.getResource();
741 }
742
743 public Role getRole() {
744 return this.role;
745 }
746
747 public void setRole(String role) {
748 this.role = Role.of(role);
749 }
750
751 public Affiliation getAffiliation() {
752 return this.affiliation;
753 }
754
755 public void setAffiliation(String affiliation) {
756 this.affiliation = Affiliation.of(affiliation);
757 }
758
759 public long getPgpKeyId() {
760 if (this.pgpKeyId != 0) {
761 return this.pgpKeyId;
762 } else if (realJid != null) {
763 return getAccount().getRoster().getContact(realJid).getPgpKeyId();
764 } else {
765 return 0;
766 }
767 }
768
769 public void setPgpKeyId(long id) {
770 this.pgpKeyId = id;
771 }
772
773 public Contact getContact() {
774 if (fullJid != null) {
775 return getAccount().getRoster().getContactFromContactList(realJid);
776 } else if (realJid != null) {
777 return getAccount().getRoster().getContact(realJid);
778 } else {
779 return null;
780 }
781 }
782
783 public boolean setAvatar(Avatar avatar) {
784 if (this.avatar != null && this.avatar.equals(avatar)) {
785 return false;
786 } else {
787 this.avatar = avatar;
788 return true;
789 }
790 }
791
792 public String getAvatar() {
793 if (avatar != null) {
794 return avatar.getFilename();
795 }
796 Avatar avatar = realJid != null ? getAccount().getRoster().getContact(realJid).getAvatar() : null;
797 return avatar == null ? null : avatar.getFilename();
798 }
799
800 public Account getAccount() {
801 return options.getAccount();
802 }
803
804 public Conversation getConversation() {
805 return options.getConversation();
806 }
807
808 public Jid getFullJid() {
809 return fullJid;
810 }
811
812 @Override
813 public boolean equals(Object o) {
814 if (this == o) return true;
815 if (o == null || getClass() != o.getClass()) return false;
816
817 User user = (User) o;
818
819 if (role != user.role) return false;
820 if (affiliation != user.affiliation) return false;
821 if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
822 return false;
823 return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
824
825 }
826
827 public boolean isDomain() {
828 return realJid != null && realJid.getLocal() == null && role == Role.NONE;
829 }
830
831 @Override
832 public int hashCode() {
833 int result = role != null ? role.hashCode() : 0;
834 result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
835 result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
836 result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
837 return result;
838 }
839
840 @Override
841 public String toString() {
842 return "[fulljid:" + String.valueOf(fullJid) + ",realjid:" + String.valueOf(realJid) + ",affiliation" + affiliation.toString() + "]";
843 }
844
845 public boolean realJidMatchesAccount() {
846 return realJid != null && realJid.equals(options.account.getJid().asBareJid());
847 }
848
849 @Override
850 public int compareTo(@NonNull User another) {
851 if (another.getAffiliation().outranks(getAffiliation())) {
852 return 1;
853 } else if (getAffiliation().outranks(another.getAffiliation())) {
854 return -1;
855 } else {
856 return getComparableName().compareToIgnoreCase(another.getComparableName());
857 }
858 }
859
860 public String getComparableName() {
861 Contact contact = getContact();
862 if (contact != null) {
863 return contact.getDisplayName();
864 } else {
865 String name = getName();
866 return name == null ? "" : name;
867 }
868 }
869
870 public Jid getRealJid() {
871 return realJid;
872 }
873
874 public void setRealJid(Jid jid) {
875 this.realJid = jid != null ? jid.asBareJid() : null;
876 }
877
878 public boolean setChatState(ChatState chatState) {
879 if (this.chatState == chatState) {
880 return false;
881 }
882 this.chatState = chatState;
883 return true;
884 }
885
886 @Override
887 public int getAvatarBackgroundColor() {
888 final String seed = realJid != null ? realJid.asBareJid().toString() : null;
889 return UIHelper.getColorForName(seed == null ? getName() : seed);
890 }
891 }
892}