I am working on a chatIM client for Android devices and I am having the following problem:
12:34 A goes disconnects from XMPP server
12:36 B send A a message --> A receives GCM message and connects to server to download message --> B sees A's last active time as 12:36
B should see A's last active time as 12:34, not 12:36. Even though A had to connect to the server to download the new message, the user did not actively open the app.
I believe the problem is that when you login (public synchronized void login(CharSequence username, String password, String resource)), there is no way of setting the presence to silent.
Does anyone know of a way to login to your server silently using the Smack library?
Related
I am trying to create an Android chat client using ejabberd XMPP server (19.02), Smack library (4.2.4) and Android SDK 25 using Android Studio.
I followed the example app found here: https://www.blikoontech.com/tutorials/android-smack-xmpp-introductionbuilding-a-simple-client
All is working well and I can send messages between two different Android devices running that sample app.
In ejabberd, there are options to send messages to the clients directly from the server using a CLI tool called ejabberdctl or ejabberd REST API. When I sent messages that way, the Android client doesn’t receive those messages. I tried with other clients like Conversations and Gajim and they could all receive it. I am pretty sure messages sent using those methods arrived because they were received as offline messages (on ejabberd web admin) when sent to offline clients.
Here is the part of the Android (java) code (roosterconnection.java from that sample app) that is to receive incoming messages. Please suggest me if I am missing anything. Thanks a lot.
ChatManager.getInstanceFor(mConnection).addIncomingListener(new IncomingChatMessageListener() {
#Override
public void newIncomingMessage(EntityBareJid messageFrom, Message message, Chat chat) {
///ADDED
Log.d(TAG,"message.getBody() :"+message.getBody());
Log.d(TAG,"message.getFrom() :"+message.getFrom());
String from = message.getFrom().toString();
String contactJid="";
if ( from.contains("/"))
{
contactJid = from.split("/")[0];
Log.d(TAG,"The real jid is :" +contactJid);
Log.d(TAG,"The message is from :" +from);
}else
{
contactJid=from;
}
//Bundle up the intent and send the broadcast.
Intent intent = new Intent(RoosterConnectionService.NEW_MESSAGE);
intent.setPackage(mApplicationContext.getPackageName());
intent.putExtra(RoosterConnectionService.BUNDLE_FROM_JID,contactJid);
intent.putExtra(RoosterConnectionService.BUNDLE_MESSAGE_BODY,message.getBody());
mApplicationContext.sendBroadcast(intent);
Log.d(TAG,"Received message from :"+contactJid+" broadcast sent.");
///ADDED
}
});
Here is a possible explanation, based in my experiments with a desktop client, Tkabber:
I login to ejabberd using Tkabber client, account user1#localhost, resource tka1, priority -3. The negative priority in this experiment is important.
Then I execute the command to send to full JID, including the correct resource:
ejabberdctl send_stanza aaa#localhost user1#localhost/tka1
"<message>..."
The client receives the stanza correctly.
Now I send to bare JID (without providing resource), and another setting another resource:
ejabberdctl send_stanza aaa#localhost user1#localhost
"<message>..."
ejabberdctl send_stanza aaa#localhost user1#localhost/sdsd
"<message>..."
In those cases, none of them are received by the client, because the resource doesn't match, and because its priority is negative. I can see those messages stored offline in the database.
In your client, maybe you have to add another call to set the presence online, with a positive priority.
Techs: Laravel + JS + Android + Ratchet + FCM Push NOtification + PWA
1. The first method (API)
user get all messages from get API and send the message in post API
Problem: the user has to refresh the page to check the new message
Solution: Web Socket
2. The second method (API + WebSocket)
The first time we get all history from API and send the message using the socket and save them using post API, now chat automatically get updated on message event of the Web Socket
Problem 1(Can be ignored): Web Socket is running on a secure server with wss and android client below version 4.4 are not connecting to wss.(We have to accept all SSL which is dangerous).
Problem 2(Important): PWA or on browser like chrome and firefox on android not connecting to web socket.
3. Now FCM come to the rescue
Solutions: Work on every platform
Problem: Third party, Not so real-time, does not work in countries like China, Pakistan, Iran etc...(where Google is banned). And I have to leave my idea of using web socket(This was our first attempt).
I am working on IBM Mobilefirst native android app. I have written code to enable push notification. I am getting notification but I have an issue here.
On Launch of the app I am calling following code.
final WLClient client = WLClient.getInstance();
push = client.getPush();
ResponseListener listener = new ResponseListener(ResponseListener.AUTHENTICITY_CONNECT);
client.getPush().setOnReadyToSubscribeListener(listener);
challengeHandler = new AndroidChallengeHandler(realm);
client.registerChallengeHandler(challengeHandler);
WLRequestOptions options=new WLRequestOptions();
options.setAppUserId("sample");
client.connect(listener,options);
When I launch the app for the first time all the above code gets executed and the listener calls the below method
#Override
public void onReadyToSubscribe() {
WLClient.getInstance().getPush().registerEventSourceCallback(pushAliasName, "PushAdapter","PushEventSource", this);
}
After this listener is getting executed i am calling subscribe method. I get success for the subscription to push.
On the server side I call the procedure to send the push notification and it reaches the phone.
Now when my app goes to background and I get a notification . I click on the notification and app restarts and never call onRecieve method of the registered interface.
On click of the notification it relaunches the app and call the onReadyToSubscribe() again and it never calls the onRecieve method. what should I do to get the onReceive() method to be called and app shouldn't get relaunched(if app is already in background) on click of notification?
My server side security test is as below
<customSecurityTest name="AuthSecurityTest">
<test realm="wl_antiXSRFRealm" step="1"/>
<test realm="wl_authenticityRealm" step="1"/>
<test realm="wl_remoteDisableRealm" step="1"/>
<test realm="wl_anonymousUserRealm" isInternalUserID="true" step="1"/>
<test realm="wl_deviceAutoProvisioningRealm" step="2" isInternalDeviceID="true"/>
</customSecurityTest>
The useridentity is not binded to security test but i am putting it in the apps applicationdescriptor. So it never call for credentials required of challenge handler on connect.
Here I think this could be the issue in the sample code that is provided by MFP in 7.1 version security test has useridentity realm but in my case I am not using the custom useridentity realm but I am using the default wl_anonymousUserRealm. This is the issue because when i tried working with the sample code it works completely fine with all the scenarios. But with the wl_anonymousUserRealm I have this issue.
onReadyToSubscribe() method getting called everytime the application connects to the server ( even if already subscribed for push) is expected and by design.
onReadyToSubscribe() is a call back that fires at the client denoting the token exchange between client and server is complete. This is required because tokens issued to an application by mediator can change. This makes it imperative that the server always stays updated with the latest token. If not , push notification will fail with invalid token error.
When the client connects to the server , they compare tokens - client presents the token it has now and server presents what it had stored. Three cases happen:
1) In a new registration , server does not have a token. Here client passes on what it got from the mediator, server persists it and issues a success. Client fires the onReadyToSubscribe() callback to indicate push handshake is complete and client can now subscribe to aliases or tags.
2) Client has received a new token from the mediator. This does not match the one server already has. Client passes on the new token to server, server updates its records with the new token and lets the client know. onReadyToSubscribe() call back fires at client indicating successful handshake. Earlier subscriptions records stay.
3)If the tokens match, then onReadyToSubscribe() fires denoting that push handshake is complete (no changes required)
Thanks to Vivin for the suggestions.
Here was the issue in the sample code that is provided by MFP in 7.1 version security test has useridentity realm but in my case I am not using the custom useridentity realm but I am using the default wl_anonymousUserRealm.
The actual issue was wl_anonymousUserRealm generates a new random userid everytime. So when ever my app is relaunched my userid gets changed and I wont get push in onrecieve because it thinks a different user is logged into the app..
Now because I cant change the wl_anonymousUserRealm I am sending push using deviceid. Please check the below link(Adapter code as below).
https://www.ibm.com/support/knowledgecenter/SSHS8R_7.1.0/com.ibm.worklight.apiref.doc/html/refjavascript-server/html/WL.Server.html#sendMessage
check sample client code from below link.
https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.1/notifications/push-notifications-overview/push-notifications-native-android-applications/tag-based-notifications-in-native-android-applications/
I am working on a project it is like a chatting system over TCP/IP. Android users as Clients connect to server, server code is written in Python. I want to explain what actually my problem is clearly.
Say there are two clients (users) A and B.
When Both are connected to server, the sock.accept returns two soccket connection objects, say "Aconn" and "Bconn".
like below
Aconn , Aaddress = sock.accept();
Bconn , Baddress = sock.accept();
So Now if I want to send a Message from A to B. then Server can forward received message from A to B easily as the object for B is available as "Bconn". we can send the message to B as below.
Bconn.send(datafromA);
Problem comes when B is not connected to server yet(Now Offline). B may connect to server in the future(Online). Now how to resolve this issue ? Is it possible to store that socket connection when users connect to server first time and assigning the same whenever they come online ? Any good solution? Thanks In advance
You should store messages in a file , Database , array , etc.
after B Connected to server , you can send that stored message to B Client.in order to detect specific Client , you should set specific id for each client.
In my chat app I send push notifications for new messages. Normally when app receives push it will send request to server to fetch info for new messages. My problem rise when user is offline for long time, and lot of pushes accumulated for that user. Then when he comes back online he receives all pushes at once.
So when many pushes come my app starts to make request for each push.
How can I process all pushes first and then make single request to server.
Edit 1
To fetch new messages from server I build request where I put last message id I have locally. Then server will return me all new messages after that message id.
This is my retrofit code.
public interface GroupChatFromDateInterface {
#GET("/groups/{groupId}/show_messages.json")
List<Message> groupMessages(#Path("groupId") int groupId,
#Query("auth_token") String token,
#Query("after_message_id") int messageId);
}
The easiest way to handle this issue is to use a collapse_key in the GCM message you send.
If all your GCM messages have the same collapse_key, when a device is offline the GCM server would store only one message for that collapse_key.
When the device is back online, it will receive just one GCM message, and your app will make a single request to your server.