send and parse Chat States to and from conferences

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java          |  2 
src/main/java/eu/siacs/conversations/entities/MucOptions.java            | 35 
src/main/java/eu/siacs/conversations/generator/MessageGenerator.java     |  2 
src/main/java/eu/siacs/conversations/parser/MessageParser.java           | 27 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java |  6 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        |  5 
6 files changed, 63 insertions(+), 14 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -193,7 +193,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 	}
 
 	public boolean setOutgoingChatState(ChatState state) {
-		if (mode == MODE_MULTI) {
+		if (mode == MODE_MULTI && getNextCounterpart() != null) {
 			return false;
 		}
 		if (this.mOutgoingChatState != state) {

src/main/java/eu/siacs/conversations/entities/MucOptions.java 🔗

@@ -7,8 +7,10 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.xml.Namespace;
+import eu.siacs.conversations.xmpp.chatstate.ChatState;
 import eu.siacs.conversations.xmpp.forms.Data;
 import eu.siacs.conversations.xmpp.forms.Field;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -49,6 +51,18 @@ public class MucOptions {
 		return mAutoPushConfiguration;
 	}
 
+	public boolean isSelf(Jid counterpart) {
+		return counterpart.getResourcepart().equals(getActualNick());
+	}
+
+	public void resetChatState() {
+		synchronized (users) {
+			for(User user : users) {
+				user.chatState = Config.DEFAULT_CHATSTATE;
+			}
+		}
+	}
+
 	public enum Affiliation {
 		OWNER("owner", 4, R.string.owner),
 		ADMIN("admin", 3, R.string.admin),
@@ -154,6 +168,7 @@ public class MucOptions {
 		private long pgpKeyId = 0;
 		private Avatar avatar;
 		private MucOptions options;
+		private ChatState chatState = Config.DEFAULT_CHATSTATE;
 
 		public User(MucOptions options, Jid from) {
 			this.options = options;
@@ -319,6 +334,14 @@ public class MucOptions {
 		public Jid getRealJid() {
 			return realJid;
 		}
+
+		public boolean setChatState(ChatState chatState) {
+			if (this.chatState == chatState) {
+				return false;
+			}
+			this.chatState = chatState;
+			return true;
+		}
 	}
 
 	private Account account;
@@ -521,6 +544,18 @@ public class MucOptions {
 		}
 	}
 
+	public ArrayList<User> getUsersWithChatState(ChatState state) {
+		synchronized (users) {
+			ArrayList<User> list = new ArrayList<>();
+			for(User user : users) {
+				if (user.chatState == state) {
+					list.add(user);
+				}
+			}
+			return list;
+		}
+	}
+
 	public List<User> getUsers(int max) {
 		ArrayList<User> subset = new ArrayList<>();
 		HashSet<Jid> jids = new HashSet<>();

src/main/java/eu/siacs/conversations/generator/MessageGenerator.java 🔗

@@ -155,7 +155,7 @@ public class MessageGenerator extends AbstractGenerator {
 	public MessagePacket generateChatState(Conversation conversation) {
 		final Account account = conversation.getAccount();
 		MessagePacket packet = new MessagePacket();
-		packet.setType(MessagePacket.TYPE_CHAT);
+		packet.setType(conversation.getMode() == Conversation.MODE_MULTI ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT);
 		packet.setTo(conversation.getJid().toBareJid());
 		packet.setFrom(account.getJid());
 		packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -47,20 +47,29 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 		super(service);
 	}
 
-	private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
+	private boolean extractChatState(Conversation c, final boolean isTypeGroupChat, final MessagePacket packet) {
 		ChatState state = ChatState.parse(packet);
-		if (state != null && conversation != null) {
-			final Account account = conversation.getAccount();
+		if (state != null && c != null) {
+			final Account account = c.getAccount();
 			Jid from = packet.getFrom();
 			if (from.toBareJid().equals(account.getJid().toBareJid())) {
-				conversation.setOutgoingChatState(state);
+				c.setOutgoingChatState(state);
 				if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
-					mXmppConnectionService.markRead(conversation);
+					mXmppConnectionService.markRead(c);
 					activateGracePeriod(account);
 				}
 				return false;
 			} else {
-				return conversation.setIncomingChatState(state);
+				if (isTypeGroupChat) {
+					MucOptions.User user = c.getMucOptions().findUserByFullJid(from);
+					if (user != null) {
+						return user.setChatState(state);
+					} else {
+						return false;
+					}
+				} else {
+					return c.setIncomingChatState(state);
+				}
 			}
 		}
 		return false;
@@ -396,9 +405,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 			return;
 		}
 
-		if (!isTypeGroupChat
-				&& query == null
-				&& extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) {
+		if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), isTypeGroupChat, packet)) {
 			mXmppConnectionService.updateConversationUi();
 		}
 
@@ -411,7 +418,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
 			}
 
 			if (isTypeGroupChat) {
-				if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
+				if (conversation.getMucOptions().isSelf(counterpart)) {
 					status = Message.STATUS_SEND_RECEIVED;
 					isCarbon = true; //not really carbon but received from another resource
 					if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId)) {

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -2138,7 +2138,11 @@ public class XmppConnectionService extends Service {
 	private void switchToForeground() {
 		final boolean broadcastLastActivity = broadcastLastActivity();
 		for (Conversation conversation : getConversations()) {
-			conversation.setIncomingChatState(ChatState.ACTIVE);
+			if (conversation.getMode() == Conversation.MODE_MULTI) {
+				conversation.getMucOptions().resetChatState();
+			} else {
+				conversation.setIncomingChatState(Config.DEFAULT_CHATSTATE);
+			}
 		}
 		for (Account account : getAccounts()) {
 			if (account.getStatus() == Account.State.ONLINE) {

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -828,7 +828,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 	}
 
 	protected void privateMessageWith(final Jid counterpart) {
-		this.mEditMessage.setText("");
+		if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+			activity.xmppConnectionService.sendChatState(conversation);
+		}
+		this.mEditMessage.getEditableText().clear();
 		this.conversation.setNextCounterpart(counterpart);
 		updateChatMsgHint();
 		updateSendButton();