Android asmack: stateChange never called

937 Views Asked by At

I am building a chat application using asmack xmpp client. Chat works fine and I have implemented ChatStateListener but stateChanged method never gets called. Currently to get composing status I am parsing the message xml. Below is the message format for composing,text, active.

<message id='5ec7d' to='[email protected]' from='[email protected]/682e3641' type='chat'><composing xmlns="http://jabber.org/protocol/chatstates" /></message>

<message id='9a93f22' to='[email protected]' from='[email protected]/682e3641' type='chat'><body>hi</body><active xmlns="http://jabber.org/protocol/chatstates" /></message>

<message to='[email protected]' from='[email protected]/682e3641' type='chat'><active xmlns="http://jabber.org/protocol/chatstates" /></message>

But parsing xml to get the composing status is not a good idea. Can someone help me to understand why stateChanged never get called.

2

There are 2 best solutions below

3
On

I am working on a project right now using aSmack as well, this is how I solved the problem, hopefully it helps you out. I assume that you have created an instance of ChatStateManager such as:

ChatStateManager chatStateManager = ChatStateManager.getInstance(connection);

Then to send the composing state, where connection is your current xmpp connection and currentChat is the Chat you created for the current conversation



    @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
                if(connection != null){
                    try {
                        chatStateManager.setCurrentState(ChatState.composing, currentChat);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

The other client will send you a Packet with the different states, in the case below is a composing state

<message id='16vn2-83' to='jabberusername@ip-address' from='jabberusername@ip-address' type='chat'><thread>781de2f5-8883-4b16-a3b2-3bf7aff1efe9</thread><composing xmlns="http://jabber.org/protocol/chatstates" /></message>

Now this is where it gets fun, (to answer your question). I grab every incoming Packet and send it to a BroadcatsReceiver to notify me of it. Note that if the incoming packet has a null body that means it's not an actual message with text but a ChatState message.

if (packet.getBody() == null) {

                Intent i = new Intent();
                i.setAction(Constants.ACTION_TYPING_LISTENER);
                i.putExtra(Constants.ACTION_EXTRA_WHO_TYPING, getSimpleUsername(packet.getFrom()));
                if (isIncomingComposingMessage(msg.toXML().toString())) {

                    i.putExtra(Constants.ACTION_EXTRA_MESSAGESTATE, ChatState.composing.toString());
                } else {
                    i.putExtra(Constants.ACTION_EXTRA_MESSAGESTATE, ChatState.paused.toString());
                }
                sendBroadcast(i);
            }

And

 public boolean isIncomingComposingMessage(String xmlMessage) {

        if (xmlMessage.indexOf(ChatState.composing.toString()) == -1) {
            return false;
        } else {
            return true;
        }

    }

I know this might be just a "workaround" and if somebody reading this has a better answer please post it so we can all learn from it.

Thank you and I hope it helps.

0
On

If you look-up the Asmack lib then you can see method updateChatState(Chat paramChat, ChatState paramChatState) will check whether your current state are same as old state , if yes then that method return false and this is the reason why you are not getting callback every time .

Why this happens ?

//Asmack lib method 
private boolean updateChatState(Chat paramChat, ChatState paramChatState) {
    ChatState localChatState = (ChatState) this.chatStates.get(paramChat);
    if (localChatState != paramChatState) {
        this.chatStates.put(paramChat, paramChatState);
        return true;
    }
    return false;
}

If you want to receive callback every time you can do something like below ,

    public class ChatlListener implements MessageListener, ChatStateListener{
        @Override
        public void stateChanged(Chat paramChat, ChatState paramChatState) {
               // this method will call only if status change , not on same status 
        }

        @Override
        public void processMessage(Chat paramChat, Message paramMessage) {

            ChatStateExtension chatStateExtension = (ChatStateExtension) paramMessage.getExtension("http://jabber.org/protocol/chatstates") ;
            Log(TAG,"Current chat status : " + chatStateExtension.getElementName());
        }
}