kobbycoder kobbycoder - 3 months ago 24
Android Question

Typing Status Smack 4.1

Hey I am trying to get typing status when the other person starts writing even though it is returning an empty message with the state as a message but it is coming from proccessMessage method. Shouldn't it be returned in the stateChanged? I haven't enabled any PacketProvider though, Do i have to do it so this would be returned in the stateChanged method? I would appreciate any help.

newChat = chatmanager.createChat(contactUsername + sc.DOMAIN, new ChatStateListener() {
@Override
public void stateChanged(Chat chat, ChatState state) {
if (ChatState.composing.equals(state)) {
Log.d("Chat State",chat.getParticipant() + " is typing..");
} else if (ChatState.gone.equals(state)) {
Log.d("Chat State",chat.getParticipant() + " has left the conversation.");
} else {
Log.d("Chat State",chat.getParticipant() + ": " + state.name());
}
}

@Override
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message);
}
});


Logs:

06-15 14:28:47.679 9949-9983/com.example.example I/System.outīš• Received message: <message to='+931111111111@example.com' from='+930000000000@example.com/Spark 2.6.3' id='AUJG0-42' type='chat'><thread>097uC9</thread><inactive xmlns='http://jabber.org/protocol/chatstates'/></message>

Answer

This is a general answer, there might be better ones, but this works for sure, it might contain stuff you already know, but I guess it can't hurt.

Base assumption - composing messages are messages where the body is null, and the xml of the whole message contains a ChatState tag with one of active, composing, paused, inactive, gone values, and what you're looking for is composing, and it's counterpart, paused.

That being said, on the receiving component you need something like this (modify it to your needs):

yourComponent.addMessageListener(new PacketListener()
            {
                @Override
                public void processPacket(Packet packet) throws NotConnectedException
                {
                    try
                    {
                        Message msg = (Message) packet;
                        String msg_xml = msg.toXML().toString();

                        if (null != msg.getBody())
                        {
                          //handle as a regular chat message....
                        }
                        else
                        {
                            if (msg_xml.contains(ChatState.composing.toString()))
                            {
                              //handle is-typing, probably some indication on screen
                            }
                            else if (msg_xml.contains(ChatState.paused.toString()))
                            {
                             // handle "stopped typing" 
                            }
                       }
                    }
                    catch (Exception e)
                    {
                     e.printStackTrace();
                    }
               }
            });

EDIT:

Your log message shows you got an inactive message, so for starters, try filtering all the messages, and find the pattern of what your looking for, to see if the sending side is even sending what you need.

A good way to understand would be to use another client that you know for a fact that's sending the composing state, and see what you get in your filter (e.g. under linux you could use pidgin or empathy for the sending side).

EDIT 2:

As per requested, I'm also adding some of the sending-side components for a more complete picture, again, this is a general example for reference purpose, and could be optimized and changed.

First, implement a custom EditText that implements a TextWatcher with a small thread that fires with the delay you want from the last text change to say "I stopped typing", set your interval as you want:

public class MyIsTypingEditText extends EditText implements TextWatcher
{
  private static final int TypingInterval = 800;

  //your listener interface that you implement anonymously from the Activity
  public interface OnTypingModified
  {
      public void onIsTypingModified(EditText view, boolean isTyping);
  }

  private OnTypingModified typingChangedListener;

  //members you would need for the small thread that declares that you have stopped typing
  private boolean currentTypingState = false;
  private Handler handler = new Handler();
  private Runnable stoppedTypingNotifier = new Runnable()
  {
      @Override
      public void run() 
      {
          //part A of the magic...
          if(null != typingChangedListener)
          {
            typingChangedListener.onIsTypingModified(MyIsTypingEditText.this, false);
            currentTypingState = false;
          }
      }
   };

  public MyIsTypingEditText(Context context)
  {
      super(context);
      this.addTextChangedListener(this);
  }

  public void setOnTypingModified(OnTypingModified typingChangedListener)
  {
      this.typingChangedListener = typingChangedListener;
  }

  @Override
  public void afterTextChanged(Editable s)
  {
      //part B of the magic...
      if(null != typingChangedListener)
      {
          if(!currentTypingState)
          {
              typingChangedListener.onIsTypingModified(this, true);
              currentTypingState = true;
          }

          handler.removeCallbacks(stoppedTypingNotifier);
          handler.postDelayed(stoppedTypingNotifier, TypingInterval);
      }
  }

  @Override
  public void beforeTextChanged(CharSequence s, int start, int count,   int after) { }


  @Override
  public void onTextChanged(CharSequence text, int start,   int before, int after) { }

  }

Next, in the activity that handles your chat, add a new anonymous interface and implement it's callback with your state-changing-message sending method call, this part C of the magic, as this will send your state corresponding to the TextWatcher mini-thread:

    yourIsTypingEditText.setOnTypingModified(new MyIsTypingEditText.OnTypingModified()
    {
        @Override
        public void onIsTypingModified(EditText view, boolean isTyping)
        {
            XmppConnectionClass.Composing composing = (isTyping) ? XmppConnectionClass.Composing.isTyping : XmppConnectionClass.Composing.stoppedTyping;

            XmppConnectionClass.getInstance().sendIsComposing(composing);
        }
    });

And last but not least, in your connection handler class you should have the following:

//.... other stuff

//a mini enum Composing class for state setting
public enum Composing
{
  private ChatState state;

  Composing(ChatState state)
  {
      this.state = state;
  }

  isTyping(ChatState.composing), stoppedTyping(ChatState.paused);

    public ChatState getState()
    {
        return (state);
    }
}

 //.... other stuff

 public boolean sendIsComposing(Composing composing)
{
    try
    {
        Message msg = new Message(...);

        //other stuff...

        msg.setBody(null);
        msg.addExtension(new ChatStateExtension(composing.getState()));

        //other stuff...

        this.sendMessage(msg);
        return (true);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        return (false);
    }
}

Hope this Helps.