I am trying to implement a real-time pushing from my server to a customized android device. (A device which runs on android OS). I can't use GCN because there is no unique token.
Here is the deal.
Problem
I am posting a message from a web portal to the server
I would like the message to be pushed to the android device
Here are the proposed solution, which is the best?
Solution 1
Insert the message with a 'create_date' timestamp
Let the android device req the server every second to check for any new messages via the 'create_date'
Solution 2
Use Pusher with pusher-java-client library installed in the android device.
I am thinking of using solution 2, which is the most ideal, I think! Since I am not too sure how Pusher works, is the architecture of pusher the same as Solution 1?
Any kind souls would answer my question?
Thank you in advance.
The architecture is different in two ways:
The Pusher service will create a persistent connection between the mobile devices and the service so when new data is available it can be instantly pushed to the mobile device
Pusher offers you real-time infrastructure so instead of having to handle multiple polling requests you make a single HTTP request to Pusher and it will push the message you send to the mobile client. This is an easier architecture than building it yourself and you see significant benefits if you need to horizontally scale.
The Code
In the Android app:
Pusher pusher = new Pusher(YOUR_APP_KEY);
pusher.connect();
// Subscribe to a channel
Channel channel = pusher.subscribe("my-channel");
// Bind to listen for events called "my-event" sent to "my-channel"
channel.bind("my-event", new SubscriptionEventListener() {
#Override
public void onEvent(String channel, String event, String data) {
System.out.println("Received event with data: " + data);
}
});
On the Web Server (node):
var Pusher = require('pusher');
var pusher = new Pusher({appId: ID, key: KEY, secret: SECRET});
pusher.trigger('my-channel', 'my-event', {some: 'data'});
To ensure that channels can only be subscribed to by the intended recipient of the message you should use authenticated channels.
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.
I'm developing a chat application using Firebase Database in Android.
I've already done the core (chat and user's list activities), but I have yet to do the notification system.
What I want is that when a user is added to a conversation (single or group) and another user writes a new message to the conversation, the first user has to receive a notification that if clicked opens the conversation activity.
My big dilemma is how to structure the service that runs in background to receive the push notification or to listen to the firebase database node I need to look at to know if there are any messages.
I figured out two different approaches:
Approach 1) Use the firebase notification
With this approach I simply send a data notification from the sender client to all the other clients in the conversation when the sender sends a message, and so the receiver will decide to show the notification (if the chat activity it's not opened) and handle the click action.
With this approach I think I will save CPU consumption and then battery.
What I don't understand is how to register a user to receive a group notification (topic notification) because as I understood, I have to subscribe that client to the topic, but if the application is in background or close, how does it knows that a new group, with its user inside, has been created and so it has to subscribe to the topic?
For the two-users conversation scenario this is not a problem as I can send the notification directly to the destination user without needing him to be subscribed to any topic.
Approach 2) Listen to a firebase database data node with a background service
With this approach I just need to create a bootable service that listen to a specific node of the database (with ValueEventListener) and show a notification when data shows that a new message/conversation is coming.
An example of the node to listen to, can be the data about the unseen messages as following:
conversation_user_unseen_messages
$conversationId1
$user1: 3
$conversationId2
$user2: 1
Then, if the data shows new messages/conversations the android app client will decide to show a system notification.
I think that with this approach there will be more energy consumption as it has to constantly check if there are any new message on the db.
Final consideration
I have found a very useful guide written by the mythical Frank van Puffelen,that explains how to set up the system I need, with using an additional server side component (node.js server).
My last question is: do I need to set up a server? Is a better solution than handling everything by the clients (using for example http requests)?
What solution do you think is the best?
Many thanks.
EDIT
I'm still figuring it out, but here it is some consideration.
I have to requesting and using a InstanceID
Instance ID provides a unique ID per instance of your apps.
So i have to request an InstanceID when user is connected and the InstanceId it is avalaible.
And then don't use topics.
Topic messages are optimized for throughput rather than latency. For
fast, secure delivery to single devices or small groups of devices,
target messages to tokens, not topics.
as said in the topic messagin guide that instead suggests to target message to tokens .
To do so I have to collect the user token in my user database reference:
users: {
$userId1: {
name: "John",
email: "john#gmail.com",
token: "Ax8HiP3Edf7....",
}
}
and then when my app client send a new message it has to also has to send a notification for all users involved in the chat, thing that I already can do with my current db structure.
How do I handle and collect the requests?
I implement a node.js server app that connect to Firebase Database and listens for the notification requests made by the app and then sends the notification request by http call to every destination app.
When do I have to register the user token?
When a app client starts for the first time or when the InstanceID expire (onTokenRefresh).
Is this the right way to do it?
EDIT 2
I found a big hole in the FCM implementation. It seems that I can not handle at all notifications delivered to iOs apps that are not in foreground.
As found in the Data notification documentation
On iOS, FCM stores the message and delivers it only when the app is in the foreground and has established a FCM connection. On Android, a client app receives a data message in onMessageReceived() and can handle the key-value pairs accordingly.
And I need to catch the data notification even when the app is in background, I need that specifically because I want to update my badge counter on the app icon to let the user know how many unread messages he has.
I'm now tryng the OneSignal solution can receive notification even when in background, it's free and interfaces with GCM. I'm sad to not stay with Google but if I can't update the badge count using FCM I have to look to an other side.
Any consideration will be appreciated.
Approach 1 is the one that you should use. Frank's guide is using the first approach, so you need to set up a server.
Is it a better solution than handling everything by the clients (using for example http requests)?
Yes. If you send the notification in the client (the app), your API Key will be exposed (via network sniffing or reverse engineering) and you definitely would want to avoid that.
how to subscribe a user to a new group topic if the app is closed or in the background?
Looks like you have to create relation mapping on the server, by calling https://iid.googleapis.com/iid/v1/IID_TOKEN/rel/topics/TOPIC_NAME with Authorization: key=YOUR_API_KEY as the header, the full description is here. Follow this guide to get the Instance ID token.
I hope my answer answers your questions. Cheers :)
Now you can simply achieve this using firebase functions ...
Here's mine
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendFollowerNotification =
functions.database.ref('/whatever/{groupUID}/{userUID}/{todoUID}')
.onWrite(async (change, context) => {
const useruuid = context.params.userUID;
const thisdata = change.after.val();
console.log('sendto:'+useruuid);
var ref = admin.database().ref(`userdatas/${useruuid}/phonetkn`);
return ref.once("value", function(snapshot){
const payload = {
notification: {
image: "default",
sound:"default",
vibrate:"true" ,
title: 'New Mission !',
body: thisdata.titre ,
color: '#22c064',
icon: "notification_icon"
}
};
admin.messaging().sendToDevice(snapshot.val(), payload)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
});
//
I am using AWS resources for my android project, I am planning to add push notification service for my project with AWS SNS.there are few questions bothering me much. I did not find any questions regarding these, except one or two but with unclear explanations.
1.Does AWS support FCM? SNS work with GCM. But Google recommends to use FCM instead of GCM. I did not find AWS supporting FCM.
2.Do AWS store messages (or data) into their databases even after sending push notifications?
3.I tried putting FCM api key in SNS application platform, it is showing invalid parameters why?
FCM is backwards compatible with GCM. The steps for setting up FCM on AWS are identical to the GCM set up procedure and (at least for the moment) FCM works transparently with GCM and SNS with respect to server-side configuration.
However, if you are sending data payloads to the Android device they will not be processed unless you implement a client side service that extends FirebaseMessagingService. The default JSON message generator in the AWS console sends data messages, which will be ignored by your app unless the aforementioned service is implemented. To get around this for initial testing you can provide a custom notification payload which will be received by your device (as long as your app is not in the foreground)
There are GCM-FCM migration instructions provided by Google however the changes you need to make are predominantly on the App side.
The steps you need to follow to test GCM/FCM on your app with SNS are:
Create a Platform Application in SNS, selecting Google Cloud Messaging (GCM) as the Push Notification Platform, and providing your Server API key in the API key field.
Select the Platform Application and click the Create platform endpoint button.
Provide the InstanceID (Device Token) generated by your app. You must extend the FirebaseInstanceIDService and override the onTokenRefresh method to see this within your Android App. Once you have done this, uninstall and reinstall your app and your token should be printed to the Debug console in Android Studio on first boot.
Click the Add endpoint button.
Click on the ARN link for your platform application.
Select the newly created Endpoint for your device and click the Publish to endpoint button.
Select the JSON Message Format, and click the JSON message generator button.
Enter a test message and click the Generate JSON button
Now comes the "gotcha part".
The message that is generated by SNS will be of the form:
{
"GCM": "{ \"data\": { \"message\": \"test message\" } }"
}
As we mentioned earlier, data payloads will be ignored if no service to receive them has been implemented. We would like to test without writing too much code, so instead we should send a notification payload. To do this, simply change the JSON message to read:
{
"GCM": "{ \"notification\": { \"title\": \"test title\", \"body\": \"test body\" } }"
}
(For more information about the JSON format of an FCM message, see the FCM documentation.)
Once you have done this, make sure your app is not running on the device, and hit the Publish Message button. You should now see a notification pop up on your device.
You can of course do all this programmatically through the Amazon SNS API, however all the examples seem to use the data payload so you need to keep that in mind and generate a payload appropriate to your use case.
Now you can go to your firebase console (https://console.firebase.google.com/) select your project, click the gear icon and choose project settings, then click on the cloud messaging tab...
You'll see the legacy Server Key which is the GCM API Key and you'll have the option to generate new Server Keys which are the FCM versions
SNS will accept both versions but their menu option is still categorizing it under GCM
Here is picture for your reference:
Note that you can "accidentally" remove your Server Keys but the Legacy server key is not deletable. Also, if you click the add server key button, you'll get a new server key BELOW the first one, WITH NO WARNING! ...Nice job Google ;)
One more additional note to Nathan Dunn's great answer.
How to send data with the notification from SNS to Firebase.
We need to add data to the Json (inside the notification):
{
"default": “any value",
"GCM": "{ \"notification\": { \"body\": \”message body\”, \”title\”: \”message title \”, \"sound\":\"default\" } , \"data\" : {\”key\" : \”value\", \”key2\" : \”value\” } }”
}
In your FirebaseMessagingService implementation (Xamarin example)
public override void OnMessageReceived(RemoteMessage message)
{
try
{
var body = message?.GetNotification()?.Body;
var title = message?.GetNotification()?.Title;
var tag = message?.GetNotification()?.Tag;
var sound = message?.GetNotification()?.Sound;
var data = message?.Data
foreach (string key in data.Keys)
{
// get your data values here
}
}
catch (Exception e)
{
}
}
I tried to use solution with notification payload instead of data, but I did not receive push notifications on the mobile device. I found this tutorial https://youtu.be/iBTFLu30dSg with English subtitles of how to use FCM with AWS SNS step by step and example of how to send push notifications from AWS console and implement it on php with aws php sdk. It helped me a lot.
Just an additional note to Nathan Dunn's Answer: to add sound use the following JSON message
{
"GCM": "{ \"notification\": { \"text\": \"test message\",\"sound\":\"default\" } }"
}
It took me a while to figure out how to send the notification with the right payload (publish to topic). So I will put it here.
private void PublishToTopic(string topicArn)
{
AmazonSimpleNotificationServiceClient snsClient =
new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.EUWest1);
PublishRequest publishRequest = new PublishRequest();
publishRequest.TopicArn = topicArn;
publishRequest.MessageStructure = "json";
string payload = "\\\"data\\\":{\\\"text\\\":\\\"Test \\\"}";
publishRequest.Message = "{\"default\": \"default\",\"GCM\":\"{" + payload + "}\"}";
PublishResponse publishResult = snsClient.Publish(publishRequest);
}
Amazon does support FCM as all previous code has been migrated from GCM to FCM. Below article explains in detail.
Article Published by Amazon
To answer the questions:
AWS SNS does support FCM.
No AWS does not store messages after sending push notifications.
For a detailed tutorial on setting up FCM with SNS please read this article.
I'm developing an app which users can send a notification to another one.
Moreover, I'm using php scripts to retrieve and sent data to my Mysql database.
Using Volley, a user can inserts a new row into table 'Notifications', but I don't know how to listen that insert from the app.
I mean, now the user who sent the notification in the database should call the method getNotificationsFromDB() to get notifications and see them on a RecyclerView (like Facebook Notifications). There's no other way. And only can know about Notifications if he is running the app.
For example, a Notification could be like this: 'John has sent you a notification'.
Also, this thread runs on the Notifications Fragment...
final Thread thread = new Thread(new Runnable() {
#Override
public void run() {
getNotificacionsFromDB();
try {
Thread.sleep(100000);
run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
But it should consume a lot of resources from device. A friend told me it could be with Triggers running on 'the runtime' of android. But actually I didn't understand him.
How can I listen updates from my MySql DB to create a Push Notification?
Is there a way using Services like IntentService? or how SocialApps like Facebook or twitter manage the database updates?
Is GCM a good option?
Just like Bruno said it is required to have a server in order for your app to receive GCM messages and vice versa. The docs have an example using Java Smack library. But you don't necessarily have to use Java. Since the upstream message you send to the server from the Android devices MUST be xmpp protocol if you decide to use php then you might wanna look into xampp server which supports xmpp protocol. Good luck.
Your architecture is quite unorthodox. You can't have GCM messages without a server pushing those GCM messages. Are you sure you don't want to build a server in between your device and database? Your setup looks very vulnerable to attacks
I need to send a push notificiation from a webapp to an android app.
Basically I just want to inform the android client that new data is available on the server. So it only has to be a string + vibration/sound of the phone.
The (big) problem here is that I am inside a corporate network without access to the internet. This is why I cannot use GCM.
So far I found the following options to accomplish the task without GCM:
Use XMPP
WebSockets
Ajax Polling
Is it possible to include WebSockets or AjaxPolling into a native Android app to trigger events like vibrate?
Is there an easier solution, without that much overhead as with xmpp, since I just need to send a short notification? So far I understand that I need somethink like SMACK XMPP for Android + e.g. Openfire and XMPPHP on the server side for this scenario.
Since nobody replied, I want to provide you with an approach I now try to pursue.
I use Laravel to write the API. It comes with an out-of-the-box support for Redis, which is awesome to Broadcasts Events -> https://laravel.com/docs/5.1/events
The following is just a quick example:
Redis::publish('test', json_encode($data));
Those events are received from a Socket.IO Server-Instance running on the same machine.
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('test');
....
server.listen(3000);
Socket.IO has an android client implementation, which allows me to connect to the socket.io server in a native android app.
Here is an example: http://socket.io/blog/native-socket-io-and-android/
private Socket mSocket;
{
try {
mSocket = IO.socket("http://localhost:3000");
} catch (URISyntaxException e) {}
}