/*
* Copyright 2014 OpenMarket Ltd
* Copyright 2017 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.androidsdk.data;
import android.text.TextUtils;
import org.matrix.androidsdk.rest.callback.ApiCallback;
import org.matrix.androidsdk.rest.callback.SimpleApiCallback;
import org.matrix.androidsdk.rest.model.ThirdPartyIdentifier;
import org.matrix.androidsdk.util.Log;
import com.google.gson.JsonObject;
import org.matrix.androidsdk.MXDataHandler;
import org.matrix.androidsdk.call.MXCallsManager;
import org.matrix.androidsdk.rest.model.Event;
import org.matrix.androidsdk.rest.model.PowerLevels;
import org.matrix.androidsdk.rest.model.RoomMember;
import org.matrix.androidsdk.rest.model.RoomThirdPartyInvite;
import org.matrix.androidsdk.rest.model.User;
import org.matrix.androidsdk.util.JsonUtils;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The state of a room.
*/
public class RoomState implements Externalizable {
private static final String LOG_TAG = "RoomState";
private static final long serialVersionUID = -6019932024524988201L;
public static final String DIRECTORY_VISIBILITY_PRIVATE = "private";
public static final String DIRECTORY_VISIBILITY_PUBLIC = "public";
public static final String JOIN_RULE_PUBLIC = "public";
public static final String JOIN_RULE_INVITE = "invite";
/**
* room access is granted to guests
**/
public static final String GUEST_ACCESS_CAN_JOIN = "can_join";
/**
* room access is denied to guests
**/
public static final String GUEST_ACCESS_FORBIDDEN = "forbidden";
public static final String HISTORY_VISIBILITY_SHARED = "shared";
public static final String HISTORY_VISIBILITY_INVITED = "invited";
public static final String HISTORY_VISIBILITY_JOINED = "joined";
public static final String HISTORY_VISIBILITY_WORLD_READABLE = "world_readable";
// Public members used for JSON mapping
// The room ID
public String roomId;
// The power level of room members
private PowerLevels powerLevels;
// The aliases
public List<String> aliases;
// The room aliases. The key is the domain.
private Map<String, Event> mRoomAliases = new HashMap<>();
// the aliases are defined for each home server url
private Map<String, List<String>> mAliasesByDomain = new HashMap();
// merged from mAliasesByHomeServerUrl
private List<String> mMergedAliasesList;
//
private Map<String, Event> mStateEvents = new HashMap<>();
// Informs which alias is the canonical one.
public String alias;
// The name of the room as provided by the home server.
public String name;
// The topic of the room.
public String topic;
// The avatar url of the room.
public String url;
public String avatar_url;
// the room creator (user id)
public String creator;
// the join rule
public String join_rule;
/**
* the guest access policy of the room
**/
public String guest_access;
// SPEC-134
public String history_visibility;
// the public room alias / name
public String roomAliasName;
/**
* the room visibility in the directory list (i.e. public, private...)
**/
public String visibility;
// the encryption algorithm
public String algorithm;
/**
* The number of unread messages that match the push notification rules.
* It is based on the notificationCount field in /sync response.
*/
private int mNotificationCount;
/**
* The number of highlighted unread messages (subset of notifications).
* It is based on the notificationCount field in /sync response.
*/
private int mHighlightCount;
// the associated token
private String token;
// the room members
private final Map<String, RoomMember> mMembers = new HashMap<>();
// the third party invite members
private final Map<String, RoomThirdPartyInvite> mThirdPartyInvites = new HashMap<>();
/**
* Cache for [self memberWithThirdPartyInviteToken].
* The key is the 3pid invite token.
*/
private final Map<String, RoomMember> mMembersWithThirdPartyInviteTokenCache = new HashMap<>();
/**
* Additional and optional metadata got from initialSync
*/
private String mMembership;
/**
* Tell if the roomstate if a live one.
*/
private boolean mIsLive;
/**
* Tell if the room is a user conference user one
*/
private Boolean mIsConferenceUserRoom = null;
// the unitary tests crash when MXDataHandler type is set.
private transient Object mDataHandler = null;
// member display cache
private transient HashMap<String, String> mMemberDisplayNameByUserId = new HashMap<>();
// get the guest access
// avoid the null case
public String getGuestAccess() {
if (null != guest_access) {
return guest_access;
}
// retro compliancy
return RoomState.GUEST_ACCESS_FORBIDDEN;
}
// get the history visibility
// avoid the null case
public String getHistoryVisibility() {
if (null != history_visibility) {
return history_visibility;
}
// retro compliancy
return RoomState.HISTORY_VISIBILITY_SHARED;
}
/**
* @return the state token
*/
public String getToken() {
return token;
}
/**
* Update the token.
*
* @param token the new token
*/
public void setToken(String token) {
this.token = token;
}
// avatar Url makes more sense than url.
public String getAvatarUrl() {
if (null != url) {
return url;
} else {
return avatar_url;
}
}
/**
* @return a copy of the room members list.
*/
public Collection<RoomMember> getMembers() {
ArrayList<RoomMember> res;
synchronized (this) {
// make a copy to avoid concurrency modifications
res = new ArrayList<>(mMembers.values());
}
return res;
}
/**
* Provides the latest state events used to create this room state.
* It includes the room member creation events (they are not loaded in memory by default).
* @return the latest state events list
*/
public void getStateEvents(final SimpleApiCallback<List<Event>> callback) {
final ArrayList<Event> stateEvents = new ArrayList<>();
stateEvents.addAll(mStateEvents.values());
// retrieve the roomMember creation events
((MXDataHandler) mDataHandler).getStore().getRoomStateEvents(roomId, new SimpleApiCallback<List<Event>>() {
@Override
public void onSuccess(List<Event> events) {
stateEvents.addAll(events);
callback.onSuccess(stateEvents);
}
});
}
/**
* Provides a list of displayable members.
* Some dummy members are created to internal stuff.
*
* @return a copy of the displayable room members list.
*/
public Collection<RoomMember> getDisplayableMembers() {
Collection<RoomMember> members = getMembers();
RoomMember conferenceUserId = getMember(MXCallsManager.getConferenceUserId(roomId));
if (null != conferenceUserId) {
ArrayList<RoomMember> membersList = new ArrayList<>(members);
membersList.remove(conferenceUserId);
members = membersList;
}
return members;
}
/**
* Tells if the room is a call conference one
* i.e. this room has been created to manage the call conference
*
* @return true if it is a call conference room.
*/
public boolean isConferenceUserRoom() {
// test if it is not yet initialized
if (null == mIsConferenceUserRoom) {
mIsConferenceUserRoom = false;
Collection<RoomMember> members = getMembers();
// works only with 1:1 room
if (2 == members.size()) {
for (RoomMember member : members) {
if (MXCallsManager.isConferenceUserId(member.getUserId())) {
mIsConferenceUserRoom = true;
break;
}
}
}
}
return mIsConferenceUserRoom;
}
/**
* Set this room as a conference user room
*
* @param isConferenceUserRoom true when it is an user conference room.
*/
public void setIsConferenceUserRoom(boolean isConferenceUserRoom) {
mIsConferenceUserRoom = isConferenceUserRoom;
}
/**
* Update the room member from its user id.
*
* @param userId the user id.
* @param member the new member value.
*/
public void setMember(String userId, RoomMember member) {
// Populate a basic user object if there is none
if (member.getUserId() == null) {
member.setUserId(userId);
}
synchronized (this) {
if (null != mMemberDisplayNameByUserId) {
mMemberDisplayNameByUserId.remove(userId);
}
mMembers.put(userId, member);
}
}
/**
* Retrieve a room member from its user id.
*
* @param userId the user id.
* @return the linked member it exists.
*/
public RoomMember getMember(String userId) {
RoomMember member;
synchronized (this) {
member = mMembers.get(userId);
}
return member;
}
/**
* Remove a member defines by its user id.
*
* @param userId the user id.
*/
public void removeMember(String userId) {
synchronized (this) {
mMembers.remove(userId);
// remove the cached display name
if (null != mMemberDisplayNameByUserId) {
mMemberDisplayNameByUserId.remove(userId);
}
}
}
/**
* Retrieve a member from an invitation token.
*
* @param thirdPartyInviteToken the third party invitation token.
* @return the member it exists.
*/
public RoomMember memberWithThirdPartyInviteToken(String thirdPartyInviteToken) {
return mMembersWithThirdPartyInviteTokenCache.get(thirdPartyInviteToken);
}
/**
* Retrieve a RoomThirdPartyInvite from its token.
*
* @param thirdPartyInviteToken the third party invitation token.
* @return the linked RoomThirdPartyInvite if it exists
*/
public RoomThirdPartyInvite thirdPartyInviteWithToken(String thirdPartyInviteToken) {
return mThirdPartyInvites.get(thirdPartyInviteToken);
}
/**
* @return the third party invite list.
*/
public Collection<RoomThirdPartyInvite> thirdPartyInvites() {
return mThirdPartyInvites.values();
}
/**
* @return the power levels (it can be null).
*/
public PowerLevels getPowerLevels() {
if (null != powerLevels) {
return powerLevels.deepCopy();
} else {
return null;
}
}
/**
* Update the power levels.
*
* @param powerLevels the new power levels
*/
public void setPowerLevels(PowerLevels powerLevels) {
this.powerLevels = powerLevels;
}
/**
* Update the linked dataHandler.
*
* @param dataHandler the new dataHandler
*/
public void setDataHandler(MXDataHandler dataHandler) {
mDataHandler = dataHandler;
}
/**
* @return the user dataHandler
*/
public MXDataHandler getDataHandler() {
return (MXDataHandler) mDataHandler;
}
/**
* Update the notified messages count.
*
* @param notificationCount the new notified messages count.
*/
public void setNotificationCount(int notificationCount) {
mNotificationCount = notificationCount;
}
/**
* @return the notified messages count.
*/
public int getNotificationCount() {
return mNotificationCount;
}
/**
* Update the highlighted messages count.
*
* @param highlightCount the new highlighted messages count.
*/
public void setHighlightCount(int highlightCount) {
mHighlightCount = highlightCount;
}
/**
* @return the highlighted messages count.
*/
public int getHighlightCount() {
return mHighlightCount;
}
/**
* Check if the user userId can back paginate.
*
* @param userId the user Id.
* @return true if the user can backpaginate.
*/
public boolean canBackPaginated(String userId) {
RoomMember member = getMember(userId);
String membership = (null != member) ? member.membership : "";
String visibility = TextUtils.isEmpty(history_visibility) ? HISTORY_VISIBILITY_SHARED : history_visibility;
return visibility.equals(HISTORY_VISIBILITY_WORLD_READABLE) ||
visibility.equals(HISTORY_VISIBILITY_SHARED) ||
(RoomMember.MEMBERSHIP_JOIN.equals(membership)) /*&&visibility == invited or joined */ ||
(RoomMember.MEMBERSHIP_INVITE.equals(membership) && visibility.equals(HISTORY_VISIBILITY_INVITED))
;
}
/**
* Make a deep copy of this room state object.
*
* @return the copy
*/
public RoomState deepCopy() {
RoomState copy = new RoomState();
copy.roomId = roomId;
copy.setPowerLevels((powerLevels == null) ? null : powerLevels.deepCopy());
copy.aliases = (aliases == null) ? null : new ArrayList<>(aliases);
copy.mAliasesByDomain = new HashMap<>(mAliasesByDomain);
copy.alias = this.alias;
copy.name = name;
copy.topic = topic;
copy.url = url;
copy.creator = creator;
copy.join_rule = join_rule;
copy.guest_access = guest_access;
copy.history_visibility = history_visibility;
copy.visibility = visibility;
copy.roomAliasName = roomAliasName;
copy.token = token;
copy.mDataHandler = mDataHandler;
copy.mMembership = mMembership;
copy.mIsLive = mIsLive;
copy.mIsConferenceUserRoom = mIsConferenceUserRoom;
copy.algorithm = algorithm;
copy.mRoomAliases = new HashMap<>(mRoomAliases);
copy.mStateEvents = new HashMap<>(mStateEvents);
synchronized (this) {
Iterator it = mMembers.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, RoomMember> pair = (Map.Entry<String, RoomMember>) it.next();
copy.setMember(pair.getKey(), pair.getValue().deepCopy());
}
Collection<String> keys = mThirdPartyInvites.keySet();
for (String key : keys) {
copy.mThirdPartyInvites.put(key, mThirdPartyInvites.get(key).deepCopy());
}
keys = mMembersWithThirdPartyInviteTokenCache.keySet();
for (String key : keys) {
copy.mMembersWithThirdPartyInviteTokenCache.put(key, mMembersWithThirdPartyInviteTokenCache.get(key).deepCopy());
}
}
return copy;
}
/**
* @return the room alias
*/
public String getAlias() {
// SPEC-125
if (!TextUtils.isEmpty(alias)) {
return alias;
} else if (!TextUtils.isEmpty(getFirstAlias())) {
return getFirstAlias();
}
return null;
}
/**
* Returns the first room alias.
*
* @return the first room alias
*/
private String getFirstAlias() {
List<String> mergedAliases = getAliases();
if (mergedAliases.size() != 0) {
return mergedAliases.get(0);
}
return null;
}
/**
* Provides the aliases for any known domains
*
* @return the aliases list
*/
public List<String> getAliases() {
if (null == mMergedAliasesList) {
mMergedAliasesList = new ArrayList<>();
for (String url : mAliasesByDomain.keySet()) {
mMergedAliasesList.addAll(mAliasesByDomain.get(url));
}
// ensure that the current aliases have been added.
// for example for the public rooms because there is no applystate call.
if (null != aliases) {
for (String anAlias : aliases) {
if (mMergedAliasesList.indexOf(anAlias) < 0) {
mMergedAliasesList.add(anAlias);
}
}
}
}
return mMergedAliasesList;
}
/**
* Provides the aliases by domain
*
* @return the aliases list map
*/
public Map<String, List<String>> getAliasesByDomain() {
return new HashMap<>(mAliasesByDomain);
}
/**
* Remove an alias.
*
* @param alias the alias to remove
*/
public void removeAlias(String alias) {
if (getAliases().indexOf(alias) >= 0) {
if (null != aliases) {
aliases.remove(alias);
}
for (String host : mAliasesByDomain.keySet()) {
mAliasesByDomain.get(host).remove(alias);
}
mMergedAliasesList = null;
}
}
/**
* Add an alias.
*
* @param alias the alias to add
*/
public void addAlias(String alias) {
if (getAliases().indexOf(alias) < 0) {
// patch until the server echoes the alias addition.
mMergedAliasesList.add(alias);
}
}
/**
* Build and return the room's display name.
*
* @param selfUserId this user's user id (to exclude from members)
* @return the display name
*/
public String getDisplayName(String selfUserId) {
String displayName = null;
String alias = getAlias();
synchronized (this) {
if (name != null) {
displayName = name;
} else if (!TextUtils.isEmpty(alias)) {
displayName = getAlias();
}
// compute a name
else if (mMembers.size() > 0) {
Iterator it = mMembers.entrySet().iterator();
Map.Entry<String, RoomMember> otherUserPair = null;
if ((mMembers.size() >= 3) && (selfUserId != null)) {
// this is a group chat and should have the names of participants
// according to "(<num> <name1>, <name2>, <name3> ..."
int count = 0;
displayName = "";
while (it.hasNext()) {
Map.Entry<String, RoomMember> pair = (Map.Entry<String, RoomMember>) it.next();
if (!selfUserId.equals(pair.getKey())) {
otherUserPair = pair;
if (count > 0) {
displayName += ", ";
}
if (otherUserPair.getValue().getName() != null) {
displayName += getMemberName(otherUserPair.getValue().getUserId()); // The member name
} else {
displayName += getMemberName(otherUserPair.getKey()); // The user id
}
count++;
}
}
displayName = "(" + count + ") " + displayName;
} else {
// by default, it is oneself name
displayName = getMemberName(selfUserId);
// A One2One private room can default to being called like the other guy
if (selfUserId != null) {
while (it.hasNext()) {
Map.Entry<String, RoomMember> pair = (Map.Entry<String, RoomMember>) it.next();
if (!selfUserId.equals(pair.getKey())) {
otherUserPair = pair;
break;
}
}
}
if (otherUserPair != null) {
if (otherUserPair.getValue().getName() != null) {
displayName = getMemberName(otherUserPair.getValue().getUserId()); // The member name
} else {
displayName = getMemberName(otherUserPair.getKey()); // The user id
}
}
}
}
}
if ((displayName != null) && (alias != null) && !displayName.equals(alias)) {
if (TextUtils.isEmpty(displayName)) {
displayName = alias;
} else {
displayName += " (" + alias + ")";
}
}
if (displayName == null) {
displayName = roomId;
}
return displayName;
}
/**
* @return true if the room is encrypted
*/
public boolean isEncrypted() {
return !TextUtils.isEmpty(algorithm);
}
/**
* @return the encryption algorithm
*/
public String encryptionAlgorithm() {
return algorithm;
}
/**
* Apply the given event (relevant for state changes) to our state.
*
* @param event the event
* @param direction how the event should affect the state: Forwards for applying, backwards for un-applying (applying the previous state)
* @return true if the event is managed
*/
public boolean applyState(Event event, EventTimeline.Direction direction) {
if (event.stateKey == null) {
return false;
}
JsonObject contentToConsider = (direction == EventTimeline.Direction.FORWARDS) ? event.getContentAsJsonObject() : event.getPrevContentAsJsonObject();
String eventType = event.getType();
try {
if (Event.EVENT_TYPE_STATE_ROOM_NAME.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
name = (roomState == null) ? null : roomState.name;
} else if (Event.EVENT_TYPE_STATE_ROOM_TOPIC.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
topic = (roomState == null) ? null : roomState.topic;
} else if (Event.EVENT_TYPE_STATE_ROOM_CREATE.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
creator = (roomState == null) ? null : roomState.creator;
} else if (Event.EVENT_TYPE_STATE_ROOM_JOIN_RULES.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
join_rule = (roomState == null) ? null : roomState.join_rule;
} else if (Event.EVENT_TYPE_STATE_ROOM_GUEST_ACCESS.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
guest_access = (roomState == null) ? null : roomState.guest_access;
} else if (Event.EVENT_TYPE_STATE_ROOM_ALIASES.equals(eventType)) {
if (!TextUtils.isEmpty(event.stateKey)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
// backward compatibility
aliases = (null == roomState) ? null : roomState.aliases;
// sanity check
if (null != aliases) {
mAliasesByDomain.put(event.stateKey, aliases);
mRoomAliases.put(event.stateKey, event);
} else {
mAliasesByDomain.put(event.stateKey, new ArrayList<String>());
}
}
} else if (Event.EVENT_TYPE_MESSAGE_ENCRYPTION.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
algorithm = (roomState == null) ? null : roomState.algorithm;
} else if (Event.EVENT_TYPE_STATE_CANONICAL_ALIAS.equals(eventType)) {
// SPEC-125
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
alias = (roomState == null) ? null : roomState.alias;
} else if (Event.EVENT_TYPE_STATE_HISTORY_VISIBILITY.equals(eventType)) {
// SPEC-134
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
history_visibility = (roomState == null) ? null : roomState.history_visibility;
} else if (Event.EVENT_TYPE_STATE_ROOM_AVATAR.equals(eventType)) {
RoomState roomState = JsonUtils.toRoomState(contentToConsider);
url = (roomState == null) ? null : roomState.url;
} else if (Event.EVENT_TYPE_STATE_ROOM_MEMBER.equals(eventType)) {
RoomMember member = JsonUtils.toRoomMember(contentToConsider);
String userId = event.stateKey;
if (member == null) {
// the member has already been removed
if (null == getMember(userId)) {
return false;
}
removeMember(userId);
} else {
member.setUserId(userId);
member.setOriginServerTs(event.getOriginServerTs());
member.setInviterId(event.getSender());
((MXDataHandler) mDataHandler).getStore().storeRoomStateEvent(roomId, event);
RoomMember currentMember = getMember(userId);
// check if the member is the same
// duplicated message ?
if (member.equals(currentMember)) {
return false;
}
// when a member leaves a room, his avatar / display name is not anymore provided
if (null != currentMember) {
if (member.membership.equals(RoomMember.MEMBERSHIP_LEAVE) || member.membership.equals(RoomMember.MEMBERSHIP_BAN)) {
if (null == member.avatarUrl) {
member.avatarUrl = currentMember.avatarUrl;
}
if (null == member.displayname) {
member.displayname = currentMember.displayname;
}
// remove the cached display name
if (null != mMemberDisplayNameByUserId) {
mMemberDisplayNameByUserId.remove(userId);
}
}
}
if ((direction == EventTimeline.Direction.FORWARDS)) {
if (null != mDataHandler) {
((MXDataHandler) mDataHandler).getStore().updateUserWithRoomMemberEvent(member);
}
}
// Cache room member event that is successor of a third party invite event
if (!TextUtils.isEmpty(member.getThirdPartyInviteToken())) {
mMembersWithThirdPartyInviteTokenCache.put(member.getThirdPartyInviteToken(), member);
}
setMember(userId, member);
}
} else if (Event.EVENT_TYPE_STATE_ROOM_POWER_LEVELS.equals(eventType)) {
powerLevels = JsonUtils.toPowerLevels(contentToConsider);
} else if (Event.EVENT_TYPE_STATE_ROOM_THIRD_PARTY_INVITE.equals(event.getType())) {
RoomThirdPartyInvite thirdPartyInvite = JsonUtils.toRoomThirdPartyInvite(contentToConsider);
thirdPartyInvite.token = event.stateKey;
((MXDataHandler) mDataHandler).getStore().storeRoomStateEvent(roomId, event);
if (!TextUtils.isEmpty(thirdPartyInvite.token)) {
mThirdPartyInvites.put(thirdPartyInvite.token, thirdPartyInvite);
}
}
mStateEvents.put(eventType, event);
} catch (Exception e) {
Log.e(LOG_TAG, "applyState failed with error " + e.getLocalizedMessage());
}
return true;
}
/**
* @return true if the room is a public one
*/
public boolean isPublic() {
return TextUtils.equals((null != visibility) ? visibility : join_rule, DIRECTORY_VISIBILITY_PUBLIC);
}
/**
* Return an unique display name of the member userId.
*
* @param userId the user id
* @return unique display name
*/
public String getMemberName(String userId) {
// sanity check
if (null == userId) {
return null;
}
String displayName;
synchronized (this) {
if (null == mMemberDisplayNameByUserId) {
mMemberDisplayNameByUserId = new HashMap<>();
}
displayName = mMemberDisplayNameByUserId.get(userId);
}
if (null != displayName) {
return displayName;
}
// Get the user display name from the member list of the room
RoomMember member = getMember(userId);
// Do not consider null display name
if ((null != member) && !TextUtils.isEmpty(member.displayname)) {
displayName = member.displayname;
synchronized (this) {
ArrayList<String> matrixIds = new ArrayList<>();
// Disambiguate users who have the same display name in the room
for (RoomMember aMember : mMembers.values()) {
if (displayName.equals(aMember.displayname)) {
matrixIds.add(aMember.getUserId());
}
}
// if several users have the same display name
// index it i.e bob (<Matrix id>)
if (matrixIds.size() > 1) {
displayName += " (" + userId + ")";
}
}
} else if ((null != member) && TextUtils.equals(member.membership, RoomMember.MEMBERSHIP_INVITE)) {
User user = ((MXDataHandler) mDataHandler).getUser(userId);
if (null != user) {
displayName = user.displayname;
}
}
if (null == displayName) {
// By default, use the user ID
displayName = userId;
}
mMemberDisplayNameByUserId.put(userId, displayName);
return displayName;
}
@Override
public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
if (input.readBoolean()) {
roomId = input.readUTF();
}
if (input.readBoolean()) {
powerLevels = (PowerLevels) input.readObject();
}
if (input.readBoolean()) {
aliases = (List<String>) input.readObject();
}
List<Event> roomAliasesEvents = (List<Event>) input.readObject();
for (Event e : roomAliasesEvents) {
mRoomAliases.put(e.stateKey, e);
}
mAliasesByDomain = (Map<String, List<String>>) input.readObject();
if (input.readBoolean()) {
mMergedAliasesList = (List<String>) input.readObject();
}
List<Event> stateEvents = (List<Event>) input.readObject();
for (Event e : stateEvents) {
mStateEvents.put(e.getType(), e);
}
if (input.readBoolean()) {
alias = input.readUTF();
}
if (input.readBoolean()) {
name = input.readUTF();
}
if (input.readBoolean()) {
topic = input.readUTF();
}
if (input.readBoolean()) {
url = input.readUTF();
}
if (input.readBoolean()) {
avatar_url = input.readUTF();
}
if (input.readBoolean()) {
creator = input.readUTF();
}
if (input.readBoolean()) {
join_rule = input.readUTF();
}
if (input.readBoolean()) {
guest_access = input.readUTF();
}
if (input.readBoolean()) {
history_visibility = input.readUTF();
}
if (input.readBoolean()) {
roomAliasName = input.readUTF();
}
if (input.readBoolean()) {
visibility = input.readUTF();
}
if (input.readBoolean()) {
algorithm = input.readUTF();
}
mNotificationCount = input.readInt();
mHighlightCount = input.readInt();
if (input.readBoolean()) {
token = input.readUTF();
}
List<RoomMember> members = (List<RoomMember>) input.readObject();
for (RoomMember r : members) {
mMembers.put(r.getUserId(), r);
}
List<RoomThirdPartyInvite> invites = (List<RoomThirdPartyInvite>) input.readObject();
for (RoomThirdPartyInvite i : invites) {
mThirdPartyInvites.put(i.token, i);
}
List<RoomMember> inviteTokens = (List<RoomMember>) input.readObject();
for (RoomMember r : inviteTokens) {
mMembersWithThirdPartyInviteTokenCache.put(r.getThirdPartyInviteToken(), r);
}
if (input.readBoolean()) {
mMembership = input.readUTF();
}
mIsLive = input.readBoolean();
if (input.readBoolean()) {
mIsConferenceUserRoom = input.readBoolean();
}
}
@Override
public void writeExternal(ObjectOutput output) throws IOException {
output.writeBoolean(null != roomId);
if (null != roomId) {
output.writeUTF(roomId);
}
output.writeBoolean(null != powerLevels);
if (null != powerLevels) {
output.writeObject(powerLevels);
}
output.writeBoolean(null != aliases);
if (null != aliases) {
output.writeObject(aliases);
}
output.writeObject(new ArrayList<>(mRoomAliases.values()));
output.writeObject(mAliasesByDomain);
output.writeBoolean(null != mMergedAliasesList);
if (null != mMergedAliasesList) {
output.writeObject(mMergedAliasesList);
}
output.writeObject(new ArrayList<>(mStateEvents.values()));
output.writeBoolean(null != alias);
if (null != alias) {
output.writeUTF(alias);
}
output.writeBoolean(null != name);
if (null != name) {
output.writeUTF(name);
}
output.writeBoolean(null != topic);
if (null != topic) {
output.writeUTF(topic);
}
output.writeBoolean(null != url);
if (null != url) {
output.writeUTF(url);
}
output.writeBoolean(null != avatar_url);
if (null != avatar_url) {
output.writeUTF(avatar_url);
}
output.writeBoolean(null != creator);
if (null != creator) {
output.writeUTF(creator);
}
output.writeBoolean(null != join_rule);
if (null != join_rule) {
output.writeUTF(join_rule);
}
output.writeBoolean(null != guest_access);
if (null != guest_access) {
output.writeUTF(guest_access);
}
output.writeBoolean(null != history_visibility);
if (null != history_visibility) {
output.writeUTF(history_visibility);
}
output.writeBoolean(null != roomAliasName);
if (null != roomAliasName) {
output.writeUTF(roomAliasName);
}
output.writeBoolean(null != visibility);
if (null != visibility) {
output.writeUTF(visibility);
}
output.writeBoolean(null != algorithm);
if (null != algorithm) {
output.writeUTF(algorithm);
}
output.writeInt(mNotificationCount);
output.writeInt(mHighlightCount);
output.writeBoolean(null != token);
if (null != token) {
output.writeUTF(token);
}
output.writeObject(new ArrayList<>(mMembers.values()));
output.writeObject(new ArrayList<>(mThirdPartyInvites.values()));
output.writeObject(new ArrayList<>(mMembersWithThirdPartyInviteTokenCache.values()));
output.writeBoolean(null != mMembership);
if (null != mMembership) {
output.writeUTF(mMembership);
}
output.writeBoolean(mIsLive);
output.writeBoolean(null != mIsConferenceUserRoom);
if (null != mIsConferenceUserRoom) {
output.writeBoolean(mIsConferenceUserRoom);
}
}
}