Detailed changes
  
  
    
    @@ -53,7 +53,8 @@ public class MucOptions {
     public MucOptions(Conversation conversation) {
         this.account = conversation.getAccount();
         this.conversation = conversation;
-        this.self = new User(this, createJoinJid(getProposedNick()));
+        final String nick = getProposedNick(conversation.getAttribute("mucNick"));
+        this.self = new User(this, createJoinJid(nick), nick);
         this.self.affiliation = Affiliation.of(conversation.getAttribute("affiliation"));
         this.self.role = Role.of(conversation.getAttribute("role"));
     }
@@ -66,6 +67,7 @@ public class MucOptions {
         this.self = user;
         final boolean roleChanged = this.conversation.setAttribute("role", user.role.toString());
         final boolean affiliationChanged = this.conversation.setAttribute("affiliation", user.affiliation.toString());
+        this.conversation.setAttribute("mucNick", user.getNick());
         return roleChanged || affiliationChanged;
     }
 
@@ -291,6 +293,20 @@ public class MucOptions {
         return false;
     }
 
+    public User findUserByName(final String name) {
+        if (name == null) {
+            return null;
+        }
+        synchronized (users) {
+            for (User user : users) {
+                if (name.equals(user.getName())) {
+                    return user;
+                }
+            }
+        }
+        return null;
+    }
+
     public User findUserByFullJid(Jid jid) {
         if (jid == null) {
             return null;
@@ -322,7 +338,7 @@ public class MucOptions {
     public User findOrCreateUserByRealJid(Jid jid, Jid fullJid) {
         User user = findUserByRealJid(jid);
         if (user == null) {
-            user = new User(this, fullJid);
+            user = new User(this, fullJid, null);
             user.setRealJid(jid);
         }
         return user;
@@ -422,11 +438,17 @@ public class MucOptions {
     }
 
     public String getProposedNick() {
+        return getProposedNick(null);
+    }
+
+    public String getProposedNick(final String mucNick) {
         final Bookmark bookmark = this.conversation.getBookmark();
         final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
         if (bookmarkedNick != null) {
             this.tookProposedNickFromBookmark = true;
             return bookmarkedNick;
+        } else if (mucNick != null) {
+            return mucNick;
         } else if (!conversation.getJid().isBareJid()) {
             return conversation.getJid().getResource();
         } else {
@@ -456,6 +478,14 @@ public class MucOptions {
     }
 
     public String getActualNick() {
+        if (this.self.getNick() != null) {
+            return this.self.getNick();
+        } else {
+            return this.getProposedNick();
+        }
+    }
+
+    public String getActualName() {
         if (this.self.getName() != null) {
             return this.self.getName();
         } else {
@@ -507,7 +537,7 @@ public class MucOptions {
     private List<User> getFallbackUsersFromCryptoTargets() {
         List<User> users = new ArrayList<>();
         for (Jid jid : conversation.getAcceptedCryptoTargets()) {
-            User user = new User(this, null);
+            User user = new User(this, null, null);
             user.setRealJid(jid);
             users.add(user);
         }
@@ -581,10 +611,18 @@ public class MucOptions {
     }
 
     public Jid createJoinJid(String nick) {
+        return createJoinJid(nick, true);
+    }
+
+    private Jid createJoinJid(String nick, boolean tryFix) {
         try {
             return conversation.getJid().withResource(nick);
         } catch (final IllegalArgumentException e) {
-            return null;
+            try {
+                return tryFix ? createJoinJid(gnu.inet.encoding.Punycode.encode(nick), false) : null;
+            } catch (final gnu.inet.encoding.PunycodeException e2) {
+                return null;
+            }
         }
     }
 
@@ -748,20 +786,26 @@ public class MucOptions {
         private Affiliation affiliation = Affiliation.NONE;
         private Jid realJid;
         private Jid fullJid;
+        private String nick;
         private long pgpKeyId = 0;
         private Avatar avatar;
         private final MucOptions options;
         private ChatState chatState = Config.DEFAULT_CHAT_STATE;
 
-        public User(MucOptions options, Jid fullJid) {
+        public User(MucOptions options, Jid fullJid, final String nick) {
             this.options = options;
             this.fullJid = fullJid;
+            this.nick = nick == null ? getName() : nick;
         }
 
         public String getName() {
             return fullJid == null ? null : fullJid.getResource();
         }
 
+        public String getNick() {
+            return nick;
+        }
+
         public Role getRole() {
             return this.role;
         }
@@ -869,7 +913,7 @@ public class MucOptions {
 
         @Override
         public String toString() {
-            return "[fulljid:" + fullJid + ",realjid:" + realJid + ",affiliation" + affiliation.toString() + "]";
+            return "[fulljid:" + fullJid + ",realjid:" + realJid + ",nick:" + nick + ",affiliation" + affiliation.toString() + "]";
         }
 
         public boolean realJidMatchesAccount() {
  
  
  
    
    @@ -132,10 +132,10 @@ public abstract class AbstractParser {
 	}
 
 	public static MucOptions.User parseItem(Conversation conference, Element item) {
-		return parseItem(conference,item, null);
+		return parseItem(conference,item,null,null);
 	}
 
-	public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid) {
+	public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid, final String nickname) {
 		final String local = conference.getJid().getLocal();
 		final String domain = conference.getJid().getDomain().toEscapedString();
 		String affiliation = item.getAttribute("affiliation");
@@ -149,7 +149,13 @@ public abstract class AbstractParser {
 			}
 		}
 		Jid realJid = item.getAttributeAsJid("jid");
-		MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid);
+		if (fullJid != null) nick = fullJid.getResource();
+		try {
+			if (nickname != null && nick != null && !nick.equals(nickname) && gnu.inet.encoding.Punycode.decode(nick).equals(nickname)) {
+				nick = nickname;
+			}
+		} catch (final gnu.inet.encoding.PunycodeException e) { }
+		MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid, nick);
 		if (InvalidJid.isValid(realJid)) {
 			user.setRealJid(realJid);
 		}
  
  
  
    
    @@ -63,6 +63,7 @@ public class PresenceParser extends AbstractParser implements
 		if (!from.isBareJid()) {
 			final String type = packet.getAttribute("type");
 			final Element x = packet.findChild("x", Namespace.MUC_USER);
+			final Element nick = packet.findChild("nick", Namespace.NICK);
 			Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
 			final List<String> codes = getStatusCodes(x);
 			if (type == null) {
@@ -70,7 +71,7 @@ public class PresenceParser extends AbstractParser implements
 					Element item = x.findChild("item");
 					if (item != null && !from.isBareJid()) {
 						mucOptions.setError(MucOptions.Error.NONE);
-						MucOptions.User user = parseItem(conversation, item, from);
+						MucOptions.User user = parseItem(conversation, item, from, nick == null ? null : nick.getContent());
 						if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && jid.equals(InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"))))) {
 							if (mucOptions.setOnline()) {
 								mXmppConnectionService.getAvatarService().clear(mucOptions);
@@ -174,7 +175,7 @@ public class PresenceParser extends AbstractParser implements
 				} else if (!from.isBareJid()){
 					Element item = x.findChild("item");
 					if (item != null) {
-						mucOptions.updateUser(parseItem(conversation, item, from));
+						mucOptions.updateUser(parseItem(conversation, item, from, nick == null ? null : nick.getContent()));
 					}
 					MucOptions.User user = mucOptions.deleteUser(from);
 					if (user != null) {
  
  
  
    
    @@ -1817,11 +1817,14 @@ public class NotificationService {
 
             final String nick = conversation.getMucOptions().getActualNick();
             final Pattern highlight = generateNickHighlightPattern(nick);
-            if (message.getBody() == null || nick == null) {
+            final String name = conversation.getMucOptions().getActualName();
+            final Pattern highlightName = generateNickHighlightPattern(name);
+            if (message.getBody() == null || (nick == null && name == null)) {
                 return false;
             }
             final Matcher m = highlight.matcher(message.getBody());
-            return (m.find() || message.isPrivateMessage());
+            final Matcher m2 = highlightName.matcher(message.getBody());
+            return (m.find() || m2.find() || message.isPrivateMessage());
         } else {
             return false;
         }
  
  
  
    
    @@ -3037,7 +3037,7 @@ public class XmppConnectionService extends Service {
 
                     final Jid joinJid = mucOptions.getSelf().getFullJid();
                     Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": joining conversation " + joinJid.toString());
-                    PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous() || onConferenceJoined != null);
+                    PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous() || onConferenceJoined != null, mucOptions.getSelf().getNick());
                     packet.setTo(joinJid);
                     Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
                     if (conversation.getMucOptions().getPassword() != null) {
@@ -3303,17 +3303,18 @@ public class XmppConnectionService extends Service {
             databaseBackend.updateConversation(conversation);
         }
 
+        final String nick = self.getNick();
         final Bookmark bookmark = conversation.getBookmark();
         final String bookmarkedNick = bookmark == null ? null : bookmark.getNick();
-        if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) {
+        if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !nick.equals(bookmarkedNick)) {
             final Account account = conversation.getAccount();
             final String defaultNick = MucOptions.defaultNick(account);
-            if (TextUtils.isEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) {
+            if (TextUtils.isEmpty(bookmarkedNick) && nick.equals(defaultNick)) {
                 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": do not overwrite empty bookmark nick with default nick for " + conversation.getJid().asBareJid());
                 return;
             }
-            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
-            bookmark.setNick(full.getResource());
+            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + nick + "' into bookmark for " + conversation.getJid().asBareJid());
+            bookmark.setNick(nick);
             createBookmark(bookmark.getAccount(), bookmark);
         }
     }
@@ -3339,7 +3340,7 @@ public class XmppConnectionService extends Service {
                 }
             });
 
-            final PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, options.nonanonymous());
+            final PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, options.nonanonymous(), nick);
             packet.setTo(joinJid);
             sendPresencePacket(account, packet);
         } else {
@@ -4183,7 +4184,7 @@ public class XmppConnectionService extends Service {
                 if (conversation.getAccount() == account && conversation.getMode() == Conversational.MODE_MULTI) {
                     final MucOptions mucOptions = conversation.getMucOptions();
                     if (mucOptions.online()) {
-                        PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous());
+                        PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous(), mucOptions.getSelf().getNick());
                         packet.setTo(mucOptions.getSelf().getFullJid());
                         connection.sendPresencePacket(packet);
                     }
@@ -5107,7 +5108,8 @@ public class XmppConnectionService extends Service {
     public void saveConversationAsBookmark(Conversation conversation, String name) {
         final Account account = conversation.getAccount();
         final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid());
-        final String nick = conversation.getJid().getResource();
+        String nick = conversation.getMucOptions().getActualNick();
+        if (nick == null) nick = conversation.getJid().getResource();
         if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
             bookmark.setNick(nick);
         }
  
  
  
    
    @@ -991,10 +991,13 @@ public class ConversationFragment extends XmppFragment
         } else if (multi && conversation.getNextCounterpart() != null) {
             this.binding.textinput.setHint(R.string.send_message);
             this.binding.textInputHint.setVisibility(View.VISIBLE);
+            final MucOptions.User user = conversation.getMucOptions().findUserByName(conversation.getNextCounterpart().getResource());
+            String nick = user == null ? null : user.getNick();
+            if (nick == null) nick = conversation.getNextCounterpart().getResource();
             this.binding.textInputHint.setText(
                     getString(
                             R.string.send_private_message_to,
-                            conversation.getNextCounterpart().getResource()));
+                            nick));
             binding.conversationViewPager.setCurrentItem(0);
         } else if (multi && !conversation.getMucOptions().participating()) {
             this.binding.textInputHint.setVisibility(View.GONE);
@@ -3889,7 +3892,7 @@ public class ConversationFragment extends XmppFragment
         }
         List<String> completions = new ArrayList<>();
         for (MucOptions.User user : conversation.getMucOptions().getUsers()) {
-            String name = user.getName();
+            String name = user.getNick();
             if (name != null && name.startsWith(incomplete)) {
                 completions.add(name + (firstWord ? ": " : " "));
             }
@@ -4093,10 +4096,9 @@ public class ConversationFragment extends XmppFragment
                     if (mucOptions.participating()
                             || ((Conversation) message.getConversation()).getNextCounterpart()
                                     != null) {
-                        if (!mucOptions.isUserInRoom(user)
-                                && mucOptions.findUserByRealJid(
-                                                tcp == null ? null : tcp.asBareJid())
-                                        == null) {
+                        MucOptions.User mucUser = mucOptions.findUserByFullJid(user);
+                        MucOptions.User tcpMucUser = mucOptions.findUserByRealJid(tcp == null ? null : tcp.asBareJid());
+                        if (mucUser == null && tcpMucUser == null) {
                             Toast.makeText(
                                             getActivity(),
                                             activity.getString(
@@ -4105,7 +4107,7 @@ public class ConversationFragment extends XmppFragment
                                             Toast.LENGTH_SHORT)
                                     .show();
                         }
-                        highlightInConference(user.getResource());
+                        highlightInConference(mucUser == null ? (tcpMucUser == null ? user.getResource() : tcpMucUser.getNick()) : mucUser.getNick());
                     } else {
                         Toast.makeText(
                                         getActivity(),
  
  
  
    
    @@ -68,7 +68,7 @@ public class MucUsersActivity extends XmppActivity implements XmppConnectionServ
             final String needle = search.toLowerCase(Locale.getDefault());
             ArrayList<MucOptions.User> filtered = new ArrayList<>();
             for(MucOptions.User user : allUsers) {
-                final String name = user.getName();
+                final String name = user.getNick();
                 final Contact contact = user.getContact();
                 if (name != null && name.toLowerCase(Locale.getDefault()).contains(needle) || contact != null && contact.getDisplayName().toLowerCase(Locale.getDefault()).contains(needle)) {
                     filtered.add(user);
  
  
  
    
    @@ -582,6 +582,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
                     while (matcher.find()) {
                         body.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                     }
+
+                    pattern = NotificationService.generateNickHighlightPattern(conversation.getMucOptions().getActualName());
+                    matcher = pattern.matcher(body);
+                    while (matcher.find()) {
+                        body.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
                 }
             }
             Matcher matcher = Emoticons.getEmojiPattern(body).matcher(body);
  
  
  
    
    @@ -71,7 +71,7 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
         viewHolder.binding.getRoot().setOnClickListener(v -> {
             final XmppActivity activity = XmppActivity.find(v);
             if (activity != null) {
-                activity.highlightInMuc(user.getConversation(), user.getName());
+                activity.highlightInMuc(user.getConversation(), user.getNick());
             }
         });
         viewHolder.binding.getRoot().setTag(user);
@@ -80,7 +80,7 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
             selectedUser = user;
             return false;
         });
-        final String name = user.getName();
+        final String name = user.getNick();
         final Contact contact = user.getContact();
         if (contact != null) {
             final String displayName = contact.getDisplayName();
  
  
  
    
    @@ -38,7 +38,7 @@ public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreview
         viewHolder.binding.getRoot().setOnClickListener(v -> {
             final XmppActivity activity = XmppActivity.find(v);
             if (activity != null) {
-                activity.highlightInMuc(user.getConversation(), user.getName());
+                activity.highlightInMuc(user.getConversation(), user.getNick());
             }
         });
         viewHolder.binding.getRoot().setOnCreateContextMenuListener(this);
  
  
  
    
    @@ -43,7 +43,7 @@ public final class MucDetailsContextMenuHelper {
             } else if (user.getRealJid() != null) {
                 name = user.getRealJid().asBareJid().toString();
             } else {
-                name = user.getName();
+                name = user.getNick();
             }
             menu.setHeaderTitle(name);
             MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, user.getConversation(), user);
  
  
  
    
    @@ -462,7 +462,7 @@ public class UIHelper {
         if (contact != null) {
             return contact.getDisplayName();
         } else {
-            final String name = user.getName();
+            final String name = user.getNick();
             if (name != null) {
                 return name;
             }
@@ -540,6 +540,10 @@ public class UIHelper {
                 if (contact != null) {
                     return contact.getDisplayName();
                 } else {
+                    if (conversation instanceof Conversation) {
+                        final MucOptions.User user = ((Conversation) conversation).getMucOptions().findUserByFullJid(message.getCounterpart());
+                        if (user != null) return getDisplayName(user);
+                    }
                     return getDisplayedMucCounterpart(message.getCounterpart());
                 }
             } else {
@@ -547,7 +551,7 @@ public class UIHelper {
             }
         } else {
             if (conversation instanceof Conversation && conversation.getMode() == Conversation.MODE_MULTI) {
-                return ((Conversation) conversation).getMucOptions().getSelf().getName();
+                return ((Conversation) conversation).getMucOptions().getSelf().getNick();
             } else {
                 final Account account = conversation.getAccount();
                 final Jid jid = account.getJid();