Issue in getting friend request using smack android in offline state - android

I am using smack api in developing a chat application in android. I am facing an issue in getting the buddy request in offline state. I am getting the buddy request received by using the following code.
LoginScreen.connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
final Presence newPresence = (Presence) packet;
final Presence.Type presenceType = newPresence.getType();
final String fromId = newPresence.getFrom();
final String toId = newPresence.getTo();
final RosterEntry newEntry = roster.getEntry(fromId);
final String name = fromId.substring(0, fromId.indexOf("#"));
if (presenceType == Presence.Type.subscribed) {
Log.i("test-chat", "#####SUBSCRIBED#########");
}
if (presenceType == Presence.Type.subscribe) {
//adding buddy request to local DB
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
if (packet instanceof Presence) {
Presence presence = (Presence) packet;
if (presence.getType().equals(Presence.Type.subscribed)
|| presence.getType().equals(Presence.Type.subscribe)
|| presence.getType().equals(Presence.Type.unsubscribed)
|| presence.getType().equals(Presence.Type.unsubscribe)
|| presence.getType().equals(Presence.Type.available)
|| presence.getType().equals(Presence.Type.unavailable)) {
return true;
}
}
return false;
}
});
This is working fine when user is online. But suppose when the user is offline another user sents a buddy request to that user. When the user comes online I am unable to get the buddy request because the listener is not getting called. The listener needs to be called, to fetch all the buddy request that was got during offline state.
Please help.

Your code is fine. This happened to me as well. Well the actual problem is you are registering the listener after the request is already received.
Try registering the listener before you login the user and you will see that the request is received even when you are offline.
This will actually solve the problem. Try this and if you still face any problem... please elaborate your situation.
Hope this helps.

Related

How can a chat application user once online again get the messages that were sent to them when they were offline

I'm developing an android chat app, using Node Js and redis to stock messages and user information. I'm using socket io for communication, and Room to store message in local database. When the user is offline, I want them to receive their messages once online again. My problem is, that when user A is offline, and user B send him many messages (let's say for instance 5 messages ), when user A is online again, he only receives the first message, and the last message 4 times. Here is what I'm doing, once the user receives a message, I update the message status in Redis from "Sent" to "Delivered". In the case when the user is offline, I stock their messages in Redis with the status of message "Sent", and once online again, I check their messages received for example from user B, if their status is "Sent", I deliver it to the user, and then it will be updted to "Delivered", as shown in the code below:
//On this event, we update the socket ID of the sender in Redis so they can
receive private messages from their contacts
socket.on('sender', (sender, destinat) =>{
tempId = socket.id;
senderId = sender;
users[sender] = sender;
users [destinat] = destinat;
//We also update the user status: online
client.hset(senderId, 'lastSeen', 'Now', function(reply){
console.log( senderId + reply);
});
//Stocking to the user socket id
client.hset(users[sender], 'tempId', tempId, function(){
console.log("Welcome " + sender);
console.log("Welcome " + tempId);
});
//Getting all the messages of the sender from users
//If the sender has any messages that hasn't received yet, they'll be sent
here
//the id of each message is compsed of two parts: the phone number of the
receiver, and the id of the message itself
(receiverPhoneNumber:idMessage)
client.keys(users [sender] + ':*', function(err, results) {
results.forEach(function(key) {
client.hgetall(key, function(err, reply){
if(err)
console.log(err);
else if(reply){
//Compare the message status: if not sent, deliver it to receiver once online
if('Sent'.localeCompare(reply.status) == 0 && users
[destinat].localeCompare(reply.fromUser) == 0) {
io.to(tempId).emit('message', reply);
}
}
});
});
});
});
After receiving messages from the server, I use Async to store them in Room Database and then display them to the user, as shown in the following code
And here is the AsyncTask Class:
class AddMessage extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
//Creating a user account
m = new Message();
m.setContent( message );
m.setTime( time );
m.setUrl( url );
m.setStatus( status );
m.setFromUser( fromUser );
m.setToUser( toUser );
m.setUsername( receiver.getUsername() );
//adding to database
DatabaseClient.getInstance(getContext()).getAppDatabase()
.messageDao()
.insert(m);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText( getContext(), "Added!", Toast.LENGTH_SHORT ).show();
}
}
I've checked that messages are received from the server to the android app correctly (by re-sending the messages again to the server once delivered to the app). I believe the problem has something to do with AsyncTask, but I just can't figure it out, any help is greatly appreciated, thank you so much.
//When receving a message
socket.on("message", new Emitter.Listener() {
#Override
public void call(final Object... args) {
if(getActivity() != null){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
try {
//extract data from fired event
idMessage = data.getString( "idMessage" );
message = data.getString("message");
fromUser = data.getString( "fromUser" );
toUser = data.getString( "toUser" );
time = data.getString( "time" );
status = data.getString( "status" );
url = data.getString( "url" );
//Here we call asyncTask to Add it to Database
addMessage = new AddMessage();
addMessage.execute( );
//We emit this event to update the status of
the message to delivered
socket.emit( "sent", idMessage, userID );
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
});
I solved the problem by switching to RxJava instead of AsyncTask. The problem had something to do with AsyncTask as it sometimes influnces the chain of data, which is not the case with RxJava, as mentioned in this link : "Another issue with AsyncTasks is if you have more than one running at once. You have no guarantee what order they’ll complete in, resulting in complex logic to check when all the tasks have completed. Even worse is the assumption that one will finish before the other, until you hit an edge case that makes the first call slower, which makes them complete in the wrong order and undesired results."

Implement receiver status(read or typing) in messaging app using smack - Android

I am creating an instant messaging app in android using smack library and openfire as a server but i cannot implement the feature of the person with whom the current user is talking to. i.e. like when user read the message or when he starts typing.
Is there any way of achieving this using smack or other ?
For knowing which user is current you must implement your own in your logic. You must use Roster (contact list in xmpp servers) to get contacts of current user and save them in database or somewhere. Then create an activity to show contacts in a list. Each contact has a unique jid that can be distinguished from others with it. So with click on each contact, send it's object(include jid) to chat-activity. In chat-activity you must get previous messages from database or MAM(archived messages in server) and you can send a message to current contact(set contact jid as To).
To achieving delivery of message you must use this link. you can set request of it with this code:
Message message = … //make your stanza
DeliveryReceiptRequest.addTo(message); //add delivery request to message
connection.sendStanza(message); //send message
then you can be notified of delivery with this code:
private void setDelRecListener() {
DeliveryReceiptManager d = DeliveryReceiptManager.getInstanceFor(connection);
d.addReceiptReceivedListener(new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
Msg msg = F.getMsgBySid(receiptId);
if (msg == null)
return;
Boolean isUpdated = F.setMsgDelivered(msg);
Log.i("m/serv/UpdateDelivery", "for: " + receiptId + (isUpdated ? " Founded&Updated" : " NotFounded"));
if (isUpdated) {
BCTool.notifyPMDelivered(msg.id, msg.conv.frnd.getBareJid());
}
}
});
}
Keep in mind that every stanza has a sid(stanza id) and you must save each corresponding sid to message model in database when send is successful. This way you can detect which message delivery you got.
- For sending chat states like composing you can use this method:
public void sendChatState(String _jid, ChatState chatState) {
try {
Message msg = new Message();
msg.addExtension(new ChatStateExtension(chatState));
msg.setTo(JidCreate.bareFrom(_jid));
msg.setType(Message.Type.chat);
connection.sendStanza(msg);
Log.e("m/service", "ChatStateSent");
} catch (SmackException.NotConnectedException | InterruptedException | XmppStringprepException e) {
Log.e("m/service", "ChatState Not Sent: " + e.getMessage());
e.printStackTrace();
}
}
You must set a timer to prevent send composing in next 5Sec and reset timer when a character typed.
Consider reading this: ChatStateNotifications

How to know typing status in 1:1 conversation in Smack 4.2 in Android?

I am building a chat application in Android. I send and receive message, but I don't know how can I know the other user status and typing status of the conversation.
I have been reading to use ChatManagerLister, but it is deprecated in this version.
In Smack 4.2 the class ChatManager does not have methods to listen chat states.
Hi used chatstates ejabberd protocol for typing status. please read this doc might be helpful you XEP-0085: Chat State Notifications
For android side you need to implement following code
Message msg= (Message) stanza;
// below ChatStateExtension for Compossing message.
ChatStateExtension state = (ChatStateExtension)msg.getExtension("http://jabber.org/protocol/chatstates");//jabber:x:event
// if state (ChatStateExtension) !=null and is composing then call listener method if not error.
if(state!=null) {
Log.d(AppConstants.ELEMENT,"ChatStateExtension : " + state.toXML());
if (state.getElementName().equals("composing")) {
if (msg.getType().equals(Message.Type.error)) {
return;
}
if (iCallBackForTypingListener != null) {
DelayInformation timestamp = (DelayInformation) msg.getExtension("delay", "urn:xmpp:delay");
if (timestamp == null)
timestamp = (DelayInformation) msg.getExtension("x", "jabber:x:delay");
if (timestamp != null && timestamp.getReason().equalsIgnoreCase("Offline Storage")){ //return if delay info is Offline Storage
return;
}
//update your typing listener
iCallBackForTypingListener.onTypingStanza(fromJID, typingSender);
}
// xmpp.updateChatState(fromJID, state.getElementName(), sender);
return;
} else if (state.getElementName().equals("paused")) {
return;
}

What are the reasons GCM ID is not available in some cases in android

Hello we are working on an android application in which GCM plays very important role in such as marketing purpose, push some important information to users etc.
It's working fine in 60-70% cases but other 30-40% it does not work. So rest of users never receive any notification which is useful for only to them.
This is the reason we are loosing users everyday. Below is my code to get the registration ID of GCM.
String msg = "";
int exceptionOccurRetry = 0;
while (exceptionOccurRetry < 5) {
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
int retry = 0;
while (retry < 5 && regid.length() == 0) {
regid = gcm.register(SENDER_ID);
++retry;
}
msg = "Device registered, registration ID=" + regid;
if (!regid.equals("")) {
// You should send the registration ID to your
// server
// over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your
// app.
sendRegistrationIdToBackend();
}
break;
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
exceptionOccurRetry++;
}
}
We are looking what are the reasons such that GCM id is not available for some users.
We know only one reason that If user device doesn't have a Google Play Services installed on user phone then it does not work.
We are looking some more reasons to solve this problem.
One of the most common reasons why it is not available is that the user does not have google play services installed or is using a blocker.
You should also note that the GCM id should be refreshed if your application version has changed. You should be saving a unique device id to SharedPreferences and always check if it is the same, otherwise you should initiate the registration process again.
It is also a good idea to refresh the id from time to time.
Our team members are trying to send the notification, if it fails, they wait about 60 miliseconds or seconds (i'm not sure) for this push notification to be send again, if it still does not work, they wait twice the time, and so on ...
And you have to evaluate the response from google, there is a error string under:
std::string error = response["results"][0]["error"].asString();
Which gives you the information if a users account has been moved to, if so you can use:
Json::Value newRegistrationId = response["results"][0]["registration_id"];
to get the new ID.
if gcm id is null try to start a background task and get the id.check the below code
private void registerInBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Object[] params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
sendRegistrationIdToBackend();
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(Object msg) {
// mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
Check if app was updated; if so, it must clear the registration ID since the existing registration ID is not guaranteed to work with the new app version.
When the application version changes, the registration id should change too. so you should save the last registration id to backend size. Maybe your problem is this.
If your question all focus on the id registration you can ignore my answer..
Sorry my answer may be the wrong answer to your question.
I just want to show there may be some other 'factor' influence the message delivery..
Do your send all message on only single recipients?
One of the most useful features in GCM is support for up to 1,000
recipients for a single message.
http://developer.android.com/training/cloudsync/gcm.html#
Sometimes our PHP also lose message sending to the registered device...
The message must less than 4K(do your GCM contains Pictures?)
You may have already read this..
Implementing GCM Client on Android
http://developer.android.com/google/gcm/client.html
Is that piece of code inside an asyncTask if not that might be your error in certain version (dont remember which) gcm registration gives NetworkOnMainThreadException for some reason, they updated that later but I had that same problem some time ago, this is the piece of code I have used hope it helps you out:
private void performRegisterGCM(){
//Check for GCM availability
if(checkPlayServices(this)){
// If this check succeeds, proceed with normal processing.
// Otherwise, prompt user to get valid Play Services APK.
gcm = GoogleCloudMessaging.getInstance(this);
String regId = mPreferences.getGcmRegistrationId();
if (regId.isEmpty()){
RegisterGCM();
}else{
log.d("regId: "+regId);
}
} else {
// Status is a random integer
GooglePlayServicesUtil.getErrorDialog(status, this, RQS_GooglePlayServices).show();
}
}
public static boolean checkPlayServices(Activity mActivity){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mActivity);
return resultCode == ConnectionResult.SUCCESS
}
private void RegisterGCM(){
new AsyncTask<Void,Void,String>() {
#Override
protected String doInBackground(Void... params) {
String regid = "";
try{
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(mContext);
}
regid = gcm.register(Util.SENDER_ID);
}catch(Exception e){
log.e(e.getMessage());
}
return regid;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
mPreferences.setGcmRegistrationId(s);
//TODO send regid to server with all the other info
sendGCMIDtoBackend(s);
}
}.execute(null, null, null);
}
Note that the checkPlayServices also gave me a lot of problems I had it like this:
public static boolean checkPlayServices(Activity mActivity){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mActivity);
if(resultCode!= ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)){
GooglePlayServicesUtil.getErrorDialog(
resultCode,
mActivity,
PLAY_SERVICES_RESOLUTION_REQUEST
).show();
} else{
log.d("DEVICE NOT SUPPORTED");
exit(true);
}
return false;
}
return true;
}
Then changed it as you can see in the first piece of code, because for some reason when it falls in isUserRecoverableError(result) it gives a lot of headaches... Everything here is from an actual working project and the code snippets were obtained in http://developer.android.com/google/gcm/client.html and modified to work correctly. Hope this helps you out, Good Luck...
How about the backend? If you delete the ID from the server's database, the user will never receive a notification unless you update the app version?

typing status / online status via GCM and xmpp

I have finished developing my IM application on android (using xmpp & GCM), and I am using the gcm for buth UpPayload and DownPayloads..
and to notify the user that his partner is online/ofline I send messaage that my xmpp server "understand"
that tell the statuse.
protected void onStart() {
super.onStart();
if(!isOnlineSent)
{SendOnlineStatus("Online");
isOnlineSent=true;}
.
.
.
.}
and the SendOnlineStatus look like:
Intent OnlineMsg = new Intent();
OnlineMsg.putExtra("action", "com.Esmaeel.sodfarim.sodfa01.MESSAGE");
String nowtime = String.valueOf(EsTools.getCurrentTime());
OnlineMsg.putExtra(ConstantsGCM.TYPECLM, ConstantsGCM.ONST);
OnlineMsg.putExtra(ConstantsGCM.STATUS_on_of, Status);
OnlineMsg.putExtra(ConstantsGCM.TO_CLM, "-01");
OnlineMsg.putExtra(ConstantsGCM.FROMCLM, UUID);
OnlineMsg.putExtra(ConstantsGCM.MESSAGE_ID_CLM, regid + nowtime);
OnlineMsg.putExtra(ConstantsGCM.NAME_CLM, "Name");
final Bundle bndl = OnlineMsg.getExtras();
new AsyncTask() {
#Override
protected String doInBackground(Object[] objects) {
if (ggcm == null) {
ggcm = GoogleCloudMessaging.getInstance(context);
}
try {
ggcm.send(PRO_ID + ConstantsGCM.GCM_SERVER, bndl.getString(ConstantsGCM.MESSAGE_ID_CLM), bndl); //// GCM_SERVER="gcm.googleapis.com"
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}.execute(null, null, null);
the server checks the value of
payload.get(ConstantsGCM.TYPECLM);
if the type ONST the server reads the value payload.get(ConstantsGCM.STATUS_on_of);
the valid valuse of payload.get(ConstantsGCM.STATUS_on_of); is "online" OR "offline".
then the server update the user status and sends the new status to all the user's friends.
the same I do with Typing status but the last step I send just to the other side of the active chat.
but I get problems like "user apear online while he is offline, and some times apears typing when he is not.
any help or more effictive Ideas?
I don't know for the typing part, but for the status, maybe you should only send to the friends something like : "you should check the status of this user" then each friends phone will get the real status directly on the server. Hope it helps

Categories

Resources