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='admin#testserver123.net' from='praveenraj#testserver123.net/682e3641' type='chat'><composing xmlns="http://jabber.org/protocol/chatstates" /></message>
<message id='9a93f22' to='admin#testserver123.net' from='praveenraj#testserver123.net/682e3641' type='chat'><body>hi</body><active xmlns="http://jabber.org/protocol/chatstates" /></message>
<message to='admin#testserver123.net' from='praveenraj#testserver123.net/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.
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.
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());
}
}
Related
I want to delete the message from history in Pubnub and I am using this code
AppController.pubNub.deleteMessages()
.channels(Arrays.asList(Constants.channelAtLogin+chat_user_id))
.start(result.getMessages().get(i).getTimetoken())
.end(result.getMessages().get(i).getTimetoken())
.async(new PNCallback<PNDeleteMessagesResult>() {
#Override
public void onResponse(PNDeleteMessagesResult result, PNStatus status) {
Log.d("delete_message",result.toString());
}
});
But message is not deleted . Please help.
It seems like you are trying to show the result variable from your call which shouldn't give any result. The proper way to check if you're getting an error is to log the status. In other words, the code you're using above in essence is the correct code and is the one supported by PubNub.
pubnub.deleteMessages()
.channels(Arrays.asList("channel_1", "channel_2"))
.start(1460693607379L)
.end(1460893617271L)
.async(new PNCallback<PNDeleteMessagesResult>() {
#Override
public void onResponse(PNDeleteMessagesResult result, PNStatus status) {
// The deleteMessages() method does not return actionable data, be sure to check the status
// object on the outcome of the operation by checking the status.isError().
Log.d("Message_Deleted", status.isError().toString());
}
});
If it isn't working the next step is to verify the data from the variables you are passing in.
I'm building an app that hooks on the stock Dialer (Marshmallow API). My goal is to get incoming and place outgoing calls, while getting a handle on the Connection objects to manipulate the Connection's methods.
I have registered PhoneAccount with the CAPABILITY_CALL_PROVIDER.
PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "CustomAccount");
builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
PhoneAccount phoneAccount = builder.build();
telecomManager.registerPhoneAccount(phoneAccount);
My account is visible inside the stock Dialer app (Settings-> Calls-> Calling Accounts) and I have enabled it.
I have a Service that monitors Phone State and on CALL_STATE_RINGING it calls TelecomManager's addNewIncomingCall() method.
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
Toast.makeText(getApplicationContext(), "Phone Is Ringing",
Toast.LENGTH_SHORT).show();
Bundle extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, incomingNumber, null);
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
telecomManager.addNewIncomingCall(phoneAccountHandle, extras);
}
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {.......}
...
}
My custom Connection Service:
#Override
public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
Toast.makeText(getApplicationContext(), "onCreateIncomingConnection called", Toast.LENGTH_SHORT).show();
Connection incomingCallCannection = createConnection(request);
incomingCallCannection.setRinging();
return incomingCallCannection;
}
#Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
Toast.makeText(getApplicationContext(), "onCreateOutgoingConnection called", Toast.LENGTH_SHORT).show();
Connection outgoingCallConnection = createConnection(request);
outgoingCallConnection.setDialing();
return outgoingCallConnection;
}
private Connection createConnection(ConnectionRequest request) {
mConnection = new Connection() {
#Override
public void onStateChanged(int state) {
super.onStateChanged(state);
}
#Override
public void onDisconnect() {
super.onDisconnect();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnectionsAvailableForConference.clear();
mConnection.destroy();
}
#Override
public void onSeparate() {
super.onSeparate();
}
#Override
public void onAbort() {
super.onAbort();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnection.destroy();
}
#Override
public void onHold() {
super.onHold();
}
#Override
public void onAnswer() {
super.onAnswer();
mConnection.setActive();
}
#Override
public void onReject() {
super.onReject();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnection.destroy();
}
};
mConnection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
mConnection.setExtras(request.getExtras());
return mConnection;
}
Now, both ConnectionService's callback methods get called on incoming and outgoing calls respectively. The problem is, when I go to the Dialer and place an outgoing call (using my PhoneAccount) I get the dialing screen (inCallUI ?), with the right caller info being shown (contact name, tel # etc..), but the line doesn't ring in my earpiece and the call is not established (the telephone number that should be receiving the call doesn't ring).
I tried returning super.onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) in the callback instead of creating my own Connection object, and I get the same behavior.
TLDR: my app communicates with the Dialer, is able to place a call and show the dialing screen, but the phone line doesn't ring and nothing happens.
I have been on this for days finding a solution. But after going through the documentation over again it clearly stated that placing outgoing call with a custom PhoneAccount does not use the phone sim service to make the call, it the app that will handle all the call operation by itself.
CAPABILITY_CALL_PROVIDER: Flag indicating that this PhoneAccount can make phone calls in place of traditional SIM-based telephony
calls.
if you need to transfer data during outgoing call you can use the Bundle to send info to the default call app.
you can read more on the documentation here.
https://developer.android.com/reference/android/telecom/PhoneAccount
https://developer.android.com/guide/topics/connectivity/telecom/selfManaged#outgoing
i have implemented quickblox successfully earlier for my android chat apps for only text chat. this time i want to send message with image as attachment. File is sending successfully ( i have checked it in quickblox admin Content panel). But in the receiver part it is not displaying image or text. I am getting nor any Exception/ error or text in logcat. So it's very difficult to trace out the wrong code, i m doing. plz help.
MyChatControlle
------------------------`private QBPrivateChatManagerListener chatManagerListener=new QBPrivateChatManagerListener() {
#Override
public void chatCreated(QBPrivateChat qbPrivateChat, boolean b) {
if(!b) {
qbPrivateChat.addMessageListener(myListener);
}
}
};`
private QBMessageListener myListener=new QBMessageListener() {
#Override
public void processMessage(QBChat qbChat, QBChatMessage qbChatMessage) {
int frm=qbChatMessage.getSenderId();
int tos=qbChatMessage.getRecipientId();
System.out.println(String.format(">>> Message received (from=%s, to=%s): %s", frm, tos, qbChatMessage.getBody()));
if (onMessageReceivedListener != null) {
onMessageReceivedListener.onMessageReceived(qbChatMessage);
}
}
#Override
public void processError(QBChat qbChat, QBChatException e, QBChatMessage qbChatMessage) { }
};
ChatActivity
MyChatController.OnMessageReceivedListener onMessageReceivedListener = new MyChatController.OnMessageReceivedListener() {
#Override
public void onMessageReceived(final QBChatMessage msg) {
try {
final String mmsg = msg.getBody();
Toast.makeText(getApplicationContext(), "Receiving..."+mmsg, Toast.LENGTH_LONG).show();
}catch (Exception e){
Toast.makeText(getApplicationContext(),"#A#B#"+e.toString(), Toast.LENGTH_LONG).show();
}
}
};
You can use latest version of QuickBlox Android SDK without chat managers. In newest versions you can use QBChatDialog model to perform all chat functions. Additionally check all parameters of your attachment, they mast be not null and not empty. Can you provide message, which didn't catch by listener (.xml from logcat)?
I have been able to create the if statement that checks for the string and it returns the toast message that i created but it keeps showing the toast message every time i open the chat. even if the most recent message doesn't contain the string I am looking for so i am assume it isn't checking to see if it is the last message received and it doesn't check to see if it is unread. the code is below. the reason i am trying to do this is because my parents share a facebook account and i want an easy way to display if the message is signed mom or dad. the code below only has the check for mom once it works i will be adding the check for dad signature. I am using the open source message client Xabber. Thank you for help.
public void setVisibleChat(String account, String user) {
final boolean remove = !AccountManager.getInstance()
.getArchiveMode(account).saveLocally();
AbstractChat chat = getChat(account, user);
if (chat == null)
chat = createChat(account, user);
else {
// Mark messages as read and them delete from db if necessary.
final ArrayList<MessageItem> messageItems = new ArrayList<MessageItem>();
for (MessageItem messageItem : chat.getMessages()) {
if (!messageItem.isRead()) {
messageItem.markAsRead();
messageItems.add(messageItem);
}
if (chat.getLastText().contains("Mom") && (!messageItem.isRead()));{
Toast.makeText(Application.getInstance(), "Message from Mom!", Toast.LENGTH_SHORT).show();
}
}
Application.getInstance().runInBackground(new Runnable() {
#Override
public void run() {
Collection<Long> ids = getMessageIds(messageItems, remove);
if (remove)
MessageTable.getInstance().removeMessages(ids);
else
MessageTable.getInstance().markAsRead(ids);
}
});
}
visibleChat = chat;
}
You've got an extra semi-colon here
if (chat.getLastText().contains("Mom") && (!messageItem.isRead())); <------
So your next block of code containing the Toast show statement will always be executed.
Remove the semi-colon
As in the title I've been able to connect to Google Game Services, exchange data between two devices and everything is running fine, except one thing: disconnection callbacks.
I tried to intercept both onPeersDisconnected and onP2PDisconnected without any success. The onP2PDisconnected method is being called in the device that get disconnected from Internet but not into device that is still online (so there is no way to tell the player that the other one got disconnected).
After the match is started it seems that the second device is never notified of the accidental disconnection. If the user close the game properly the onPeersLeft method is being called thought.
Is a ping between the two devices really necessary to overcome this "bug"? Am I doing something wrong?
Here is the code I use:
void startQuickGame() {
// quick-start a game with 1 randomly selected opponent
final int MIN_OPPONENTS = 1, MAX_OPPONENTS = 1;
Bundle autoMatchCriteria = RoomConfig.createAutoMatchCriteria(MIN_OPPONENTS,
MAX_OPPONENTS, 0);
RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
rtmConfigBuilder.setMessageReceivedListener(this);
rtmConfigBuilder.setRoomStatusUpdateListener(this);
rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
mListener.switchToScreen(R.id.screen_wait);
keepScreenOn();
resetGameVars();
getGamesClient().createRoom(rtmConfigBuilder.build());
}
And here the simple listeners:
#Override
public void onPeersDisconnected(Room room, List<String> peers) {
Log.d(TAG, "onPeersDisconnected");
updateRoom(room);
}
void updateRoom(Room room) {
Log.d(TAG, "UpdateRoom: "+room.getParticipants().size());
mParticipants = room.getParticipants();
}
#Override
public void onP2PDisconnected(String participantId) {
Log.d(TAG, "onP2PDisconnected");
}
public int getPartecipantsInRooom(){
if(mRoom != null)
return mRoom.getParticipants().size();
else
return -123456;
}
Note that calling getPartecipantsInRooom() after one of the two devices disconnects always return 2, and updateRoom never get called.
Just to be sure this might not work for you, for my applications I use this to let me know when another Participant has left the Room, and it is called immediately :
#Override
public void onPeerLeft(Room room, final List<String> participantIds) {
this.mRoomCurrent = room;
this.mRoomId = this.mRoomCurrent.getRoomId();
this.mParticipants = this.mRoomCurrent.getParticipants();
int connected = 0;
for (Participant p : room.getParticipants()) {
if(p.getStatus() == Participant.STATUS_JOINED) {
connected += 1;
}
}
final int fconnected = connected;
for (String s : listIgnoreTheseIDs) {
//checkint to see if we care anymore about this ID.. if out of game already.. nope
if(s.equals(participantIds.get(0))){
return;
}
}
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
mGHInterface.onPeerLeft(fconnected, participantIds.size());
}
});
}
No idea why there are two items, but like you, I realized the onPeersDisconnected() isn't that reliable, but onPeerLeft() normally gets back to the other devices in under 1 second.
onPeerDisconnected() handles disconnects. So if somebody is still in the application but the network connection is lost, this is called for him.
onPeerLeft() handles participants who leave a room. This is called when somebody explizit leaves the room in the application or the application is minimized, and the room is left on the androids onStop() or onDestroy() callback.
I'm making two player game. So I use this approach
#Override
public void onPeerLeft(Room room, List<String> peersWhoLeft) {
updateRoom(room);
Toast.makeText(MyLauncherActivity.this, "Other player left the game", Toast.LENGTH_LONG).show();
quitGame();
}