I am trying to learn to use azure mobile app, but I am having serious problems in using the NotificationHub. I have an Imagine subscription to Azure. I creating an android mobile app with azure backend. I have created a notification hub associated to the azure mobile app on the azure portal.
To register the app on the notification hub I used the code in this tutorial:
https://learn.microsoft.com/en-gb/azure/notification-hubs/notification-hubs-android-push-notification-google-fcm-get-started
The users are authenticated on the azure backend previuosly by using their google account, microsoft account or facebook account. New users are inserted into the table Users by the following node js code written for the table script Users.js. I want a push notification to Welcome the new User.
var azureMobileApps = require('azure-mobile-apps');
var logger = require('azure-mobile-apps/src/logger');
var table = azureMobileApps.table();
table.access = 'authenticated';
/**
* Adds the email address from the claims to the context item - used for
* insert operations
* #param {Context} context the operation context
* #returns {Promise} context execution Promise
*/
function addEmailToContext(context) {
/*
* Getting claim fields
*/
return context.user.getIdentity().then((data) => {
if( data.microsoftaccount != undefined){
context.item.email = data.microsoftaccount.claims.emailaddress;
context.item.name = data.microsoftaccount.claims.givenname;
context.item.surname = data.microsoftaccount.claims.surname;
}
if( data.google != undefined){
context.item.email = data.google.claims.emailaddress;
context.item.name = data.google.claims.givenname;
context.item.surname = data.google.claims.surname;
context.item.picture_url = data.google.claims.picture;
}
if( data.facebook != undefined){
context.item.email = data.facebook.claims.emailaddress;
context.item.name = data.facebook.claims.givenname;
context.item.surname = data.facebook.claims.surname;
}
logger.info('[tables/Users.js] --> NEW USER REGISTERED:'
+'\n\t Name:'+context.item.name
+'\n\t Surname:'+context.item.surname
+'\n\t Email:'+context.item.email);
// Execute the insert. The insert returns the results as a Promise,
// Do the push as a post-execute action within the promise flow.
return context.execute()
.then(function (results) {
// Only do the push if configured
if (context.push) {
// Mobile Apps adds a user tag when registering for push notifications
// Define the GCM payload.
var payload = {
"data": {
"message": 'Welcome '+context.item.username
}
};
context.push.gcm.send(context.user.id, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
}
// Don't forget to return the results from the context.execute()
return results;
})
.catch(function (error) {
logger.error('Error while running context.execute: ', error);
});
});
}
// CREATE - add or overwrite the authenticated user
table.insert(addEmailToContext);
module.exports = table;
According to "How to: Send push notifications to an authenticated user using tags" in the tutorial on How to use the Azure Mobile Apps Node.js SDK
"When an authenticated user registers for push notifications, a user ID tag is automatically added to the registration. "
So in the Users.js, as suggested in this tutorial I wrote the following code to send the push notification to the user.
context.push.gcm.send(context.user.id, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
With this code the push notification results to be sent successfully, but the device doesn't receive any notifications. If I use null instead of context.user.id then all devices receive the push notification correctly:
context.push.gcm.send(null, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
I also tried to invoke the following custom API to create tag when the user is registered to the hub. The invoked API is the following:
var logger = require('azure-mobile-apps/src/logger');
exports.post = function(req, res) {
logger.info('[api/registerTag.js] --> Invoked');
// Get the notification hub used by the mobile app.
var push = req.azureMobile.push,
installationId = req.get('X-ZUMO-INSTALLATION-ID'),
tags = req.body.tag.toString();
// Define an update tags operation.
var updateOperation = [{
"op": "add",
"path": "/tags",
"value": tags
}];
// Update the installation to add the new tags.
push.patchInstallation(installationId, updateOperation, function(error) {
if(error){
logger.error('[api/registerTag.js] --> An error occurred while adding'
+'the following tags: \n\t'+tags, error);
res.status(error.statusCode).send(error.detail);
} else {
logger.info('[api/registerTag.js] --> The following tags have been added'
+'to the Notification Hub: \n\t'+tags, error);
res.status(200).send(tags);
}
});
};
On the console it is printed that the tag has been added successfully. But if I then modify the Users.js code like this:
...
// Only do the push if configured
if (context.push) {
// Mobile Apps adds a user tag when registering for push notifications
var userTag = '_UserId:' + context.user.id;
logger.info("TAG "+userTag);
// Define the GCM payload.
var payload = {
"data": {
"message": 'Welcome '+context.item.username
}
};
context.push.gcm.send(userTag, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
}
...
again nothing is received. I have also tried whitelisting tags or adding them automatically using the Push section of the mobile app like shown in the image:
IMAGE LINK: i.stack.imgur.com/KBvQI.png
But the problem is still there. Hope someone can help me. Thanks.
After several times of testing, I succeeded in reproducing your issue and got the same problem. To achieve your requirement I did some modification in Android client-end:
1, Cache authentication user in the MainActivity class. Following is my code snippet. For more details you can refer here.
public static final String SHAREDPREFFILE = "temp";
public static final String USERIDPREF = "uid";
public static final String TOKENPREF = "tkn";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Create the Mobile Service Client instance, using the provided Mobile Service URL and key
mClient = new MobileServiceClient(
"https://yourwebsitename.azurewebsites.net",
this).withFilter(new ProgressFilter());
// Extend timeout from default of 10s to 20s
mClient.setAndroidHttpClientFactory(new OkHttpClientFactory() {
#Override
public OkHttpClient createOkHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setReadTimeout(20, TimeUnit.SECONDS);
client.setWriteTimeout(20, TimeUnit.SECONDS);
return client;
}
});
authenticate();
} catch (MalformedURLException e) {
createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error");
} catch (Exception e){
createAndShowDialog(e, "Error");
}
}
private void authenticate() {
// We first try to load a token cache if one exists.
if (loadUserTokenCache(mClient)) {
createTable();
register();
}
// If we failed to load a token cache, login and create a token cache
else {
// Login using the Google provider.
ListenableFuture<MobileServiceUser> mLogin = mClient.login(MobileServiceAuthenticationProvider.Google);
Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
#Override
public void onFailure(Throwable exc) {
createAndShowDialog("You must log in. Login Required", "Error");
}
#Override
public void onSuccess(MobileServiceUser user) {
createAndShowDialog(String.format("You are now logged in - %1$2s", user.getUserId()), "Success");
cacheUserToken(mClient.getCurrentUser());
createTable();
register();
}
});
}
}
private void cacheUserToken(MobileServiceUser user) {
SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString(USERIDPREF, user.getUserId());
editor.putString(TOKENPREF, user.getAuthenticationToken());
editor.commit();
}
private void register() {
NotificationsManager.handleNotifications(this, NotificationSettings.SenderId, MyHandler.class);
registerWithNotificationHubs();
}
2, In RegistrationIntentService class replace regID = hub.register(FCM_token).getRegistrationId(); with the following code:
regID = hub.register(FCM_token, prefs.getString("uid", "")).getRegistrationId();
3, Make sure add the line below to the first line within onHandleIntent method.
SharedPreferences prefs = getSharedPreferences("temp", Context.MODE_PRIVATE);
Related
I'm making chat application trying to get push notification when new message received to user from another when app is in background using send notifications between Android devices using Firebase Database, Cloud Messaging and Node.js.
I'm following this blog.Here's
[https://firebase.googleblog.com/2016/08/sending-notifications-between-android.html]
and below is my code which I tried.
#Override
public void onClick(View view) {
if (view.getId() == R.id.btnSend) {
String content = editWriteMessage.getText().toString().trim();
if (content.length() > 0) {
editWriteMessage.setText("");
Message newMessage = new Message();
newMessage.text = content;
newMessage.idSender = StaticConfig.UID;
newMessage.idReceiver = roomId;
newMessage.timestamp = System.currentTimeMillis();
FirebaseDatabase.getInstance().getReference().child("message/" + roomId).push().setValue(newMessage);
sendNotificationToUser(newMessage.idReceiver,newMessage.text);
}
}
}
public void sendNotificationToUser(String user, String message) {
Map notification = new HashMap<>();
notification.put("username", user);
notification.put("message", message);
FirebaseDatabase.getInstance().getReference().child("notificationRequests").push().setValue(notification);
}
By using above codes the username and message is getting saved in real time database in notificationRequests.
And I really don't have idea that how can the push notification is received by this line of code.
FirebaseMessaging.getInstance().subscribeToTopic("user_"+username);
And the most important where do i need to put the above code to make it work.
and also I have created the bucket to host my node.js file.
thanks in advance.
EDIT: I misunderstood your problem. Make sure that firebase messaging service is in your manifest.xml file.
Original:
Use the functions.database.ref().onCreate() method. Inside the ref() parameters put the path to your notification requests.
Here's some base code in TypeScript.
export const notificationListener = functions.database
.ref('/NotificationRequests/{notification}').onCreate((snapshot, context) =>
{
try {
admin.initializeApp();
} catch (e) {
}
//Code to send notification here
return admin.database().ref('/NotificationRequests/' + context.params.notification)
}
)
The information you need for the notification is located under snapshot.child('your path here').
See why and how this works
Is there any way to send a push notification from server when a user complete a task? For example: A todo app will notify on that date with push notification. I want to use firebase and firestore for storing user tokens.
Alarm manager can be a solution that I have found but I don't wanna use it.
Yes, you can use scheduler to send notification from server to your app:
You may follow my working code:
Emplement IJob:
public class SendNotificationViaFcm: IJob
{
public void Execute(IJobExecutionContext context)
{
bool isNotificationSent=false;
try
{
var taskToSendNotification = FirebaseCloudMessaging.SendMessage();
Task.WaitAll(taskToSendNotification);
isNotificationSent = taskToSendNotification.Result;
}
catch (Exception exception)
when (
exception is ObjectDisposedException || exception is ArgumentNullException ||
exception is AggregateException)
{
}
catch (Exception exception) when (exception is InvalidOperationException)
{
}
catch (Exception exception)
{
// ignored
}
}
}
Call FCM Api from your server:
public class FirebaseCloudMessaging
{
private static readonly Uri FcmUri = new Uri(
uriString: #"https://fcm.googleapis.com",
uriKind: UriKind.Absolute);
private const string FcmApiKey = "Your Legacy Server Key";
public static async Task<bool> SendMessage()
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = FcmUri;
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization",
"key=" + FcmApiKey);
var response = await httpClient.PostAsJsonAsync(#"/fcm/send", new
{
to = "/topics/global",
priority = "high",
data = new
{
title = "Warning",
message = "Please start app to track movemoent!"
}
//to = "/topics/global",
//priority = "high",
//notification = new
//{
// title = "Warning!",
// body = "Please start app to track movemoent!"
//}
});
Debug.Write(response.Content.ReadAsStringAsync());
var ck = response.IsSuccessStatusCode;
return response.IsSuccessStatusCode;
}
}
}
Implement schedular for your time interval:
public class Scheduler
{
public static void Start()
{
try
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();
// scheduler.Shutdown();
var sentCloudNotification = JobBuilder.Create<SendNotificationViaFcm>().Build();
var cloudNotificationTrigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInMinutes(1).RepeatForever()).Build();
scheduler.ScheduleJob(sentCloudNotification, cloudNotificationTrigger);
}
catch (SchedulerException exception)
{
Debug.Write(exception.Message);
}
catch (Exception exception)
{
Debug.Write(exception.Message);
}
}
}
Finally Run in from your Global.asax.cs
protected void Application_Start()
{
Scheduler.Start();
}
It sounds like you're looking for a tool that allows you to schedule transactional notifications. What sort of server technology are you using?
From a high level you could do something like this:
1) user adds a task in the Android application
2) android application sends request to server to save the task
3) you have some code that runs in some sort of on task save callback that schedules a block of code to run in the future using crontab, celery or something similar.
4) the block of code that runs in the future is an api call to twilio to send a push notification
relevant links: https://www.twilio.com, https://firebase.google.com/docs/cloud-messaging/, http://www.celeryproject.org/, https://en.wikipedia.org/wiki/Cron
I wonder if it's possible to send push notifications to android mobile devices whenever Firebase gets added a new child on specific entity.
For example, let's say there's an entity on Firebase called Tasks. Whenever a new task is added to that firebase collection the "child_added" event is fired and then, in some way, a push notification is sent to a mobile client.
The trigger is the child_added event. However, I'm not sure if is feasible sending push notification right from Firebase events.
You can make a very simple node.js server or a java servlet (based on your language preferences) then using firebase server sdk you can add childEventListener. When value changes you can use FCM to send push notifications using http protocol. I am using this in my app and it is very feasable and reliable.
Note: If you are using this flow for an android app then using java server sdk will be beneficial as it is almost similar to what you have on android.
EDIT: After getting some spotlight on this answer I thought to share some more info regarding same.
//example node.js server as seen on this official firebase blog
var firebase = require('firebase');
var request = require('request');
var API_KEY = "..."; // Your Firebase Cloud Server API key
firebase.initializeApp({
serviceAccount: ".json",
databaseURL: "https://.firebaseio.com/"
});
ref = firebase.database().ref();
function listenForNotificationRequests() {
var requests = ref.child('notificationRequests');
ref.on('child_added', function(requestSnapshot) {
var request = requestSnapshot.val();
sendNotificationToUser(
request.username,
request.message,
function() {
request.ref().remove();
}
);
}, function(error) {
console.error(error);
});
};
function sendNotificationToUser(username, message, onSuccess) {
request({
url: 'https://fcm.googleapis.com/fcm/send',
method: 'POST',
headers: {
'Content-Type' :' application/json',
'Authorization': 'key='+API_KEY
},
body: JSON.stringify({
notification: {
title: message
},
to : '/topics/user_'+username
})
}, function(error, response, body) {
if (error) { console.error(error); }
else if (response.statusCode >= 400) {
console.error('HTTP Error: '+response.statusCode+' - '+response.statusMessage);
}
else {
onSuccess();
}
});
}
// start listening
listenForNotificationRequests();
//example test java servlet which I made just to demonstrate this use case
#WebServlet("/TestServlet")
public class MainServlet extends HttpServlet {
* #see HttpServlet#HttpServlet()
*/
public MainServlet() {
super();
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Get context and then relative path to saved json config file from
// firebase
ServletContext context = getServletContext();
String fullPath = context.getRealPath(FILE_PATH_FOR_JSON_SERVER_AUTH);
// Check if we actually got a file from above path
if (fullPath != null) {
} else {
}
// Setup connection to firebase database here
FirebaseOptions options = new FirebaseOptions.Builder().setServiceAccount(new FileInputStream(fullPath))
.setDatabaseUrl(FIREBASE_DATABSE_URL).build();
// Check to make sure we don't initialize firebase app each time webpage
// is refreshed
if (!exists) {
// If firebase app doesn't exist then initialize it here and set
// exists to true
FirebaseApp.initializeApp(options);
exists = true;
}
// Call this to begin listening *notify* node in firebase database for notifications
addNotificationListener(request, response);
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Build apache httpclient POST request
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(ENDPOINT_URL);
//Get the required id stored in lastMsgId tree map here
if (!(chatLogs.getLastMsgIdTreeMap().isEmpty())) {
sendToID = chatLogs.getLastMsgIdTreeMap().firstKey();
lstmsg = chatLogs.getLastMsgIdTreeMap().get(sendToID);
}
//Set up a unique id with concatenating sendToID and lstmsg
uniqueID = sendToID + lstmsg;
//Set up a previous id to check with unique id. To avoid instant duplicate notifications
previousID = fcmHelper.getPreviousid();
// Check uniqueId and PreviousID beforeSending
if (!(uniqueID.equals(previousID))) {
fcmHelper.setPreviousid(uniqueID);
//Check if device token and user Id hashmap is not null
if (!(userLogs.getUserIdAndFcmTokenHashMap().isEmpty())) {
//Get the device token of sendTo Id here
deviceToken = userLogs.getUserIdAndFcmTokenHashMap().get(sendToID);
// Create JSON object for downstream data/notification
JSONObject mainNotificationJsonObj = new JSONObject();
JSONObject outerBaseJsonObj = new JSONObject();
try {
// Notification payload has 'title' and 'body' key
mainNotificationJsonObj.put(TITLE, NEW_MESSAGE_RECEIVED);
mainNotificationJsonObj.put(BODY, lstmsg);
mainNotificationJsonObj.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_TYPE_DEFAULT);
//mainNotificationJsonObj.put(TAG, fcmHelper.getFcmTagId());
System.out.println("This is sentBy id =" + fcmHelper.getFcmTagId());
// This will be used in case of both 'notification' or 'data' payload
outerBaseJsonObj.put(TO, deviceToken);
// Set priority of notification. For instant chat setting
// high will
// wake device from idle state - HIGH BATTERY DRAIN
outerBaseJsonObj.put(PRIORITY_KEY, PRIORITY_HIGH);
// Specify required payload key here either 'data' or
// 'notification'. We can even use both payloads in single
// message
outerBaseJsonObj.put(NOTIFICATION, mainNotificationJsonObj);
} catch (JSONException e) {
e.printStackTrace();
}
// Setup http entity with json data and 'Content-Type' header
StringEntity requestEntity = new StringEntity(outerBaseJsonObj.toString(),
ContentType.APPLICATION_JSON);
// Setup required Authorization header
post.setHeader(AUTHORIZATION, FIREBASE_SERVER_KEY);
// Pass setup entity to post request here
post.setEntity(requestEntity);
// Execute apache http client post response
HttpResponse fcmResponse = client.execute(post);
// Get status code from FCM server to debug error and success
System.out.println(RESPONSE_CODE + fcmResponse.getStatusLine().getStatusCode());
// Get response entity from FCM server and read throw lines
BufferedReader rd = new BufferedReader(new InputStreamReader(fcmResponse.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
if (response != null) {
// Print out the response to webpage
PrintWriter out;
out = response.getWriter();
out.println(result);
System.out.println("This is Result - " + result);
}
} else {
//Abort this process if conditions not met
post.abort();
System.out.println(THIS_MSG_ALREADY_SENT);
}
}
}
/*
* This is the main method to be called to setup notifications listener on server startup
*/
private void addNotificationListener(HttpServletRequest request, HttpServletResponse response) {
//Initialize Value event listener
lastMsgListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot arg0) {
// Clear idLastMessagerecivedhash map if not null
if (lastMsgIdTreeMap != null) {
lastMsgIdTreeMap.clear();
}
//Get lastmsg to be sent as notification here
lstmsg = (String) arg0.child(LAST_MESSAGE).getValue();
//Get sendToID here
String sendToID = (String) arg0.child(SEND_TO).getValue();
//Get Sent by ID here
sentBy = (String) arg0.child(SENT_BY).getValue();
//Set fcmTag ID here
fcmHelper.setFcmTagId(sentBy);
//Check if lstmsg is not null
if (lstmsg != null) {
// Create lastmsgTimestampHashMap here
lastMsgIdTreeMap.put(sendToID, lstmsg);
}
//Check for null again
if (lastMsgIdTreeMap != null) {
chatLogs.setLastMsgIdTreeMap(lastMsgIdTreeMap);
}
try {
doPost(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onCancelled(DatabaseError arg0) {
}
};
//Set up database reference to notify node here
messageRef = FirebaseDatabase.getInstance().getReference().child(NOTIFY);
//Add value listener to database reference here
messageRef.addValueEventListener(lastMsgListener);
}
"Java servlet is just a personal test. Some parts have been edited or removed to only give an idea about it's setup this is in no way production ready servlet please don't just copy - paste. I encourage you to understand and build your own."
Edit: you should take a look at Firebase Cloud Functions, which let you do that without having to create a Node.js server
I have a problem with my app based on AWS. When I test the following function in Amazon lambda, everything works (I get the push notification on my phone):
console.log("Loading kupa function");
var AWS = require("aws-sdk");
exports.handler = function(event, context) {
var eventText = JSON.stringify(event, null, 2);
console.log("Received event:", eventText);
var sns = new AWS.SNS();
var params = {
Message: eventText,
Subject: "Test SNS From Lambda",
TopicArn: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
sns.publish(params, context.done);
context.succeed("kupa sukces");
};
However, once I use the following method on my phone I get the "kupa sukces" log into my Android Studio but I don't get the notification on the phone. Furthermore, the "Test" on Lambda does not work anymore as wel...
Here is the code:
String lambdaRequest = "{\n\"kupa\" : \"" + true + "\"\n}";
asyncTask.delegate = wysylaczKupy.this;
asyncTask.friendFunction("friendsRequest",lambdaRequest);
}
the friendFunction is here:
public static void friendFunction(String funName, String requestContent) {
final String functionName = funName;
final String requestPayload = requestContent;
new AsyncTask<Void, Void, InvokeResult>() {
#Override
protected InvokeResult doInBackground(Void... params) {
try {
final ByteBuffer payload =
ENCODER.encode(CharBuffer.wrap(requestPayload));
final InvokeRequest invokeRequest =
new InvokeRequest()
.withFunctionName(functionName)
.withInvocationType(InvocationType.RequestResponse)
.withPayload(payload);
final InvokeResult invokeResult =
AWSMobileClient
.defaultMobileClient()
.getCloudFunctionClient()
.invoke(invokeRequest);
return invokeResult;
} catch (final Exception e) {
Log.e("LAMBDA", "AWS Lambda invocation failed : " + e.getMessage(), e);
final InvokeResult result = new InvokeResult();
result.setStatusCode(500);
result.setFunctionError(e.getMessage());
return result;
}
}
}
How can I fix this?
Thank you in advance,
Jan
Jan,
The Lambda function for publishing to an SNS Topic wasn't quite right. I modified your function and provided a default json value for testing. Just put your TopicARN in and try it out. Once you have tested using the Lambda console, then try the Android code, which I did not try.
Note that when sending a JSON payload to an SNS Topic, a default value is required. The default value is used when you don't specify a protocol specific message. For example, you are publishing to an SNS Topic with Android GCM endpoints and since your JSON payload does not contain "GCM" then all endpoints will receive your default message that you provided.
I'm not sure what you were doing with "{\n\"kupa\" : \"" + true + "\"\n}" but I'm guessing the "kupa": "true" is intended to the the data key/value for the app to handle? If so, you'll need to lookup a proper GCM payload to send both a message and data.
//Pass in the following json for testing: { "default":"some message", "kupa": "true"}
console.log("Loading kupa function");
var AWS = require("aws-sdk");
exports.handler = function(event, context, callback) {
var eventText = JSON.stringify(event, null, 2);
console.log("Received event:", eventText);
var sns = new AWS.SNS();
var params = {
Message: eventText,
MessageStructure: "json",
Subject: "Test SNS From Lambda",
TopicArn: "arn:aws:sns:us-west-2:xxxxxxxxxx:test"
};
sns.publish(params, function (err, data) {
if(err){
callback(err, null); //failed
}
callback(null, "kupa sukces"); //success
});
};
I have a very weird situation. I have an application where I am implementing a "Notification History".
I have a separate application that sends push notifications to targeted channels and then creates an entry into a table called Notifications, saving the channel that was targeted and the message that was sent.
channels = channelEditText.getText().toString();
message = messageEditText.getText().toString();
ParsePush push = new ParsePush();
push.setChannel(channels);
push.setMessage(message);
push.sendInBackground();
channelEditText.setText("");
messageEditText.setText("");
ParseObject notifications = new ParseObject("Notifications");
notifications.add("channels", channels);
notifications.put("msg", message);
notifications.saveInBackground();
My Android app's "Notification History" fragment then performs
ParseQueryAdapter<ParseObject> notificationAdapter =
new ParseQueryAdapter<ParseObject>(getActivity(), new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery<ParseObject> create() {
ParseQuery query = new ParseQuery("Notifications");
query.whereContainedIn("channels", ParseInstallation.getCurrentInstallation().getList("channels"));
query.orderByDescending("createdAt");
return query;
}
});
notificationAdapter.setTextKey("msg");
ListView notificationListView = (ListView) rootView.findViewById(R.id.notificationListView);
notificationListView.setAdapter(notificationAdapter);
My ParseApplication.java subscribes a user to channel: "Welcome" on installation so I don't receive a null pointer. The Notifications Table entry with channel "Welcome" populates the listview.
I have two ways to subscribe to a channel. One way is on the device itself like this
final EditText syncInput = (EditText) rootView.findViewById(R.id.syncInput);
Button syncButton = (Button) rootView.findViewById(R.id.syncButton);
syncButton.setOnClickListener(new OnClickListener() {
public void onClick(View v){
String sync = null;
sync = syncInput.getText().toString();
PushService.subscribe(getActivity(), sync, DashboardActivity.class);
syncInput.setText("");
}
});
The other way is through CloudCode
Parse.Cloud.define("subscribeToChannel", function(request, response){
var channelName = request.params.channel;
var userId = request.params.userId;
if(!channelName) {
response.error("Missing parameter: channel");
return;
}
if (!userId) {
response.error("Missing paremeter: userId");
return;
}
//Create a Pointer to the user based on their object id
var user = new Parse.User();
user.id = userId;
Parse.Cloud.useMasterKey();
// A user might have more than one installation
var query = new Parse.Query(Parse.Installation);
query.equalTo("user", user); //Match Installations with a pointer to this User
query.find({
success: function(installations) {
for (var i = 0; i < installations.length; i++) {
//Add the channel to al the installations for this user
installations[i].addUnique("channels", channelName);
}
//Save all the installations
Parse.Object.saveAll(installations, {
success: function(installations) {
//All the installations where saved.
response.success("All the installations were updated with this channel.");
},
error: function(error) {
//An error occured while saving one of the objects.
console.error(error);
response.error("An error occured while updating this user's installations.");
}
});
},
error: function(error) {
console.error(error);
response.error("An error occurred while looking up this user's installations");
}
});
});
Both ways of subscribing are successful in that a Push notification sent to the target channel reaches the device. Here is the issue... If I use the device to subscribe my query will show the messages sent to that channel that is saved in the Notifications table. If I use the CloudCode my query does NOT show the message sent to the channel that is saved in the Notification table.
I'm stumped. Any help is deeply appreciated.
--------------------------------SOLUTION-------------------------------------------------
protected void onResume() {
super.onResume();
ParseInstallation.getCurrentInstallation().refreshInBackground(new RefreshCallback(){
#Override
public void done(ParseObject parseObject, ParseException e) {
List<String> channels = ParseInstallation.getCurrentInstallation().getList("channels");
for (int i = 0; i < channels.size(); i++) {
Log.w("TEST", channels.get(i));
}
}
});
}
You're editing the Installation record on the server-side in Cloud Code, but the device isn't getting the updated data. If this is a common behavior in your app, refresh the installation object when you load the app:
ParseInstallation.getCurrentInstallation().refreshInBackground();
or fetchInBackground, as shown here: https://parse.com/docs/android_guide#objects-retrieving
This could also be solved by, instead of querying from the device, calling a cloud function which does the query (with the updated channels list already on the server-side.)