I am writing my own message provider to send out push notifications on both iOS and Android. I have the provider working well for iOS. My problem is Android. With the app not running, The phone gets the message and notifies the user a message came in. I only see the icon in the message. The message body is not displayed. If the app is running, the app gets the message event and I can see in the JSON, the message body and message title. So it would appear that the information is coming through. I've tried this on an Android running JellyBean and Marshmallow and get the same results. I also tried using Googles test notification from their web site for FCM. I get the same results with their web notification send. Any ideas?
Here is the code I am using in Delphi:
HttpClient.Request.URL := 'https://fcm.googleapis.com/fcm/send';
HttpClient.Request.ContentType := 'application/json';
HttpClient.Request.CustomHeaders.Add('Authorization: key = ***'); // Server Key from Google
HttpClient.Request.CharSet := 'utf-8';
JSONMsg := TJSONObject.Create;
JSONMsg.AddPair('to', devicetoken);
JSONInfo := TJSONObject.Create;
JSONInfo.AddPair('body', edtMessage.Text);
JSONInfo.AddPair('title', 'CODY Mobility');
JSONInfo.AddPair('priority', 'high');
JSONTrue := TJsonTrue.Create;
JSONFalse := TJsonFalse.Create;
JSONInfo.AddPair('content_available', JSONTrue);
JSONInfo.AddPair('dry_run', JSONFalse);
JSONToSend := TStringStream.Create(JSONMsg.ToString, TEncoding.UTF8);
StatusMemo.Lines.Add('Sending Android message to device: ' + deviceToken);
try
HttpClient.Post('https://fcm.googleapis.com/fcm/send', JSONToSend);
except
on E:Exception do begin
StatusMemo.Lines.Add('Message send failed: ' + E.Message);
end;
end;
StatusMemo.Lines.Add('Android message response: ' + HttpClient.ResponseText);
FreeAndNil(JSONTrue);
FreeAndNil(JSONFalse);
I have found my problem! It appears to be a bug with Delphi. My original message sent was:
{"to":<MyDeviceID>","notification":{"body":"It finally works!!!!!","title":"CODY Mobility Title","priority":"high","content_available":false,"dry_run":false}}
If I change my message to the following, it now works:
{"to":"<MyDeviceID>","data":{"message":"It finally works!!!!!","title":"CODY Mobility Title","priority":"high","content_available":false,"dry_run":false}}
I had to change from "notification" to "Data" and I had to change the "body" element to a "message" element. With those changes in place, the phone gets and displays the message when the app is closed. Of course, this no longer matches Google's specs for sending a notification with Firebase Cloud Messaging. I dug into the Delphi code and I found a Delphi java script called NotificationPublisher.Java. In there, there is code which specifically looks for a message element.
if (jsonVal != null) {
if (jsonVal.has("message"))
{ msg = jsonVal.optString("message"); }
else if (jsonVal.has("msg"))
{ msg = jsonVal.optString("msg"); }
else if (jsonVal.has("alert"))
{ msg = jsonVal.optString("alert"); }
if (jsonVal.has("title"))
{ title = jsonVal.optString("title"); }
}
} else {
// Look for msg or message in bundle
if (key.equals("message"))
{ msg = valstr; } else if (key.equals("msg")) { msg = valstr; }
if (key.equals("title"))
{ title = valstr; }
}
I am not sure where Delphi checks for Notification elements versus Data elements. But I believe this is part of the problem. I created a support ticket so Embarcadero can look into this further. If anyone else has this problem... Just change your message and it'll work.
Related
when my app was using Android5, I can monitor if the SMS was sent or failed by checking the content://sms/sent and content://sms/failed. Aside from via my app, I can also see the message and its status directly from the SMS app of the phone.
Now that the good old Android5 smartphone is dead, I am upgrading to a newer smartphone base on Android8 (Oreo). However I am facing a problem of detecting the status of the sent sms.
First... I can no longer "see" the sms using the phone's default SMS app.
Second... I cant also find the sms using my app even if I scroll through all messages one by one.
This is the code I use to send the SMS:
smsManager := TJSmsManager.JavaClass.getDefault;
smsTo := StringToJString(targetstr);
smsarray := smsmanager.divideMessage(stringtojstring(bodystr));
smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, nil, nil);
While this is the code I use to scroll through all my messages:
uri := StrToJURI('content://sms');
smscursor := TAndroidHelper.Activity.getContentResolver.query(uri, nil, nil,nil,nil);
id_id := smscursor.getColumnIndex(StringToJstring('_id'));
idaddress := smscursor.getColumnIndex(StringToJstring('address'));
idbody := smscursor.getColumnIndex(StringToJstring('body'));
idstatus := smscursor.getColumnIndex(StringToJstring('status'));
idthread_id := smscursor.getColumnIndex(StringToJstring('thread_id'));
idtype := smscursor.getColumnIndex(StringToJstring('type'));
smscursor.moveToFirst;
** using the repeat until loop to read each and every sms until "islast" is true **
But I cant find the sent SMS irregardless if the SMS was successfully sent or not.
I need to do this detection because the signal strength in my area is very low and around 20% of the time the sms failed, and my app should resend the intended message.
PS: I also tried searching for the SMS from the content://sms/sent and content://sms/failed but to no avail.
Anyway, my questions are:
How to make the send out sms show up inside the phone's default sms app?
How to correctly determine if the send out sms was successful or not?
I read somewhere that the method above is using the API and there is a way to use the phone's sms app instead... But I dont have any idea how to do it.
Thank you.
For the benefit of others who may be in similar situation that requires to:
(A) Able to send an SMS directly from the app without using the Phone's built-in SMS default app
(B) The SMS length can be more than the default 160 characters limit (using sendmultiparttextMessage)
(C) Able to know if the SMS was process or not (base on aResultCode of the onReceive from the PendingIntent)
Known Limitation:
Under multi-sim phone, sms is sent via the default sim
if sending to multiple phone recipients, there is no way to know which ones failed, if any. So I suggest send 1 sms to 1 recipient at a time
Doesnt detect if the intended recipient receive the sms or not as the DeliveryIntent is nil
PS: tested under Delphi Community Edition 10.4.2 and Android Oreo 8.0 and 8.1
What I did:
Create and save a unit for the BroadcastReceiver (I call it BCast.pas) from the 2nd answer from this link How read JPendingIntent in Delphi?
In your main.pas (assuming it is save as main.pas), add the following:
ADD TO PROJECT the unit created in step 1 (in my case bcast.pas)
add BCAST in uses clause
add variable for the TBroadCastReceiver (ie fbroadcast : Tbroadcastreceiver;)
make an OnReceiveBroadcast procedure
in my case:
private
{ Private declarations }
procedure OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
then under implementation, create the code for the OnReceiveBroadcast:
procedure Tmain.OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
begin
// ... put in the codes here for the result of the last sms send. //
// ... use the the value of AResultcode to do the checking //
// ... AResultcode=1 means ok, other number means possible error //
end;
initialize the fbroadcast as follows: (i put them under my main's onshow):
fbroadcast := TBroadcastReceiver.Create(OnReceiveBroadcast);
fbroadcast.addactions([StringToJString('xxx')]);
where xxx is any string that will be use to match the intent's action
Add in a procedure to sendsms:
procedure tmain.SendSMSAPI(target,messagestr:string);
var
smsManager: JSmsManager; // Androidapi.JNI.Telephony //
smsTo : JString; // Androidapi.JNI.JavaTypes //
PIntent : JPendingIntent; // Androidapi.JNI.App //
smsarray : jarraylist;
APIntent : JArraylist;
intent : JIntent;
begin
Intent := TJIntent.Create;
Intent.setAction(StringToJString('xxx'));
PIntent := TJPendingIntent.JavaClass.getBroadcast(TAndroidHelper.Context, 0, Intent, 0);
APIntent := Tjarraylist.create;
APIntent.add(PIntent);
smsManager:= TJSmsManager.JavaClass.getDefault;
smsTo := StringToJString(target); // Androidapi.Helpers //
smsarray := smsmanager.divideMessage(stringtojstring(messagestr));
smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, APIntent, nil);
end;
I am trying to get push notifications working from a sample app. Whenever I send a message from App Center, I get 0/0 messages sent and I don't see anything happening in my app.
I followed the instructions at http://learn.microsoft.com/en-us/appcenter/push/xamarin-android. At the same time I implemented App Center Analytics. That seems to work since I can send TrackEvent messages and see them in App Center. I am building my app on VS Mac.
I set up the Firebase Cloud Messaging and got the Android API Key
I added the Nuget packages.
I didn't need to update the AndroidManifest.xml file.
I didn't do anything about ProGuard since I don't use it (or know what it is)
I didn't do anything about the OnNewIntent stuff - it's not clear how to tell if that's needed or how/where to do it.
In MainActivity.c I have the AppCenter.Start(..., typeof(Push), typeof(Anayltics), typeof(Crash));
In my App.xaml.cs I have
public App()
{
InitializeComponent();
if (!AppCenter.Configured)
{
Push.PushNotificationReceived += (Sender, e) =>
{
string summary = "Title=" + e.Title + " Msg=" + e.Message;
Console.WriteLine(summary);
};
}
AppCenter.Start("androidkey" + "ios=ioskey;", typeof(Push), typeof(Analytics), typeof(Crashes));
MainPage = new NavigationPage(new MyPage());
}
In my MyPage.xaml.cs I have
protected override async void OnAppearing()
{
base.OnAppearing();
Push.PushNotificationReceived += (Sender, e) =>
{
string summary = "Title=" + e.Title + " Msg=" + e.Message;
Console.WriteLine(summary);
};
I have put the google-servicesjson file in the main folder of the Android app and set its build action to GoogleServicesJson. I had to do that by manually editing the csproj file.
I put breakpoints at both console.write... statements and then never get triggered. Like I say, I send off a message from App Center and it never arrives.
Any thoughts or suggestions would be appreciated.
Thanks
Les
I am creating an application where I am reflecting my Android notifications on mac and reply to them from mac itself. I am using NSUsernotification to do the same. Notifications work perfectly. When I click on reply button and type in text, sometimes notification.response returns nil in didActive notification delegate. Can anyone help me with that?
func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
if notification.activationType == NSUserNotification.ActivationType.replied {
print(notification.response) //This is nil sometimes. Not always
}
}
I don't have any more hair to tear out and need help..
I want to send messages to a topic (or channel) from a server (via GCM) so it reaches all devices subscribing to that topic.
Sending push messages to individual devices works fine and the format for sending to topics is the same, except for specifying a deviceToken, specify the topic in the for /topics/mytopic
This is described here:
https://developers.google.com/cloud-messaging/downstream
https://developers.google.com/cloud-messaging/topic-messaging
On the Titanium side (Android for now but the plan is IOS too later on) I register the device to a channel "myChannel". I can verify this by a success message returned and also in Appcelerators console.
Now, I simulate a server with curl, and send a message to "myChannel" to GCM. The message response with success and I receive back a message_id from GCM.
curl -H "Content-Type:application/json" -H "Authorization:key=APIKEY" --data '{"to": "/topics/myChannel","data": {"payload":{ "message": "Hi!" } }}' https://gcm-http.googleapis.com/gcm/send
My problem is that this message is not routed to the device.
I am guessing there is some syntax conversion needed between the channel subscribed to in Titanium and the one I use to GCM but I don't know.
The funny thing is, as soon I put in the deviceToken in curl it works perfectly.
Am I missing something fundamental here? Is the channel not there to route the same message to multiple devices subscribing to it?
Any help, clues are much appreciated.
My code looks like this:
// Require the module
var CloudPush = require('ti.cloudpush');
// Initialize the module
CloudPush.retrieveDeviceToken({
success: deviceTokenSuccess,
error: deviceTokenError
});
CloudPush.addEventListener('callback', receivePush);
function receivePush(evt) {
alert("Notification received: " + JSON.stringify(evt.payload));
}
function deviceTokenSuccess(e) {
Alloy.Globals.DeviceToken = e.deviceToken;
Titanium.API.info(Alloy.Globals.DeviceToken);
// Subscribe to topic
var Cloud = require("ti.cloud");
var subscribe_data = {
device_token: Alloy.Globals.DeviceToken,
channel: Alloy.Globals.topicChannelID,
type: Ti.Platform.name == 'android' ? 'android' : 'ios'
};
Cloud.PushNotifications.subscribeToken(
subscribe_data, function (e) {
if (e.success) {
Titanium.API.info('Subscribed successfully');
} else {
Titanium.API.error('Error subscribing');
}
});
}
// Something went wrong getting the device token
function deviceTokenError(e) {
Titanium.API.error('Failed ' + e.error);
}
GCM doesn't know about channels. That's something Arrow tracks and translates to the subscribed tokens. So you need to send push via the Arrow API or Appcelerator Dashboard.
http://docs.appcelerator.com/platform/latest/#!/guide/Sending_and_Scheduling_Push_Notifications
http://docs.appcelerator.com/arrowdb/latest/#!/api/PushNotifications
For a while now, I have been trying to figure out how to send push notifications. The app I have made is for Android right now, but I want to extend it to other devices once I figure this out. I've looked into various services, such as Amazon SNS, but they all neglect to include how to get the device token. They all assume you know how to do that.
So what I am asking is: how do I get a device token/registration ID for a device?
I tried using this code:
var tokenID = "";
document.addEventListener("deviceready", function(){
//Unregister the previous token because it might have become invalid. Unregister everytime app is started.
window.plugins.pushNotification.unregister(successHandler, errorHandler);
if(intel.xdk.device.platform == "Android")
{
//register the user and get token
window.plugins.pushNotification.register(
successHandler,
errorHandler,
{
//senderID is the project ID
"senderID":"",
//callback function that is executed when phone recieves a notification for this app
"ecb":"onNotification"
});
}
else if(intel.xdk.device.platform == "iOS")
{
//register the user and get token
window.plugins.pushNotification.register(
tokenHandler,
errorHandler,
{
//allow application to change badge number
"badge":"true",
//allow application to play notification sound
"sound":"true",
//register callback
"alert":"true",
//callback function name
"ecb":"onNotificationAPN"
});
}
}, false);
//app given permission to receive and display push messages in Android.
function successHandler (result) {
alert('result = ' + result);
}
//app denied permission to receive and display push messages in Android.
function errorHandler (error) {
alert('error = ' + error);
}
//App given permission to receive and display push messages in iOS
function tokenHandler (result) {
// Your iOS push server needs to know the token before it can push to this device
// here is where you might want to send the token to your server along with user credentials.
alert('device token = ' + result);
tokenID = result;
}
//fired when token is generated, message is received or an error occured.
function onNotification(e)
{
switch( e.event )
{
//app is registered to receive notification
case 'registered':
if(e.regid.length > 0)
{
// Your Android push server needs to know the token before it can push to this device
// here is where you might want to send the token to your server along with user credentials.
alert('registration id = '+e.regid);
tokenID = e.regid;
}
break;
case 'message':
//Do something with the push message. This function is fired when push message is received or if user clicks on the tile.
alert('message = '+e.message+' msgcnt = '+e.msgcnt);
break;
case 'error':
alert('GCM error = '+e.msg);
break;
default:
alert('An unknown GCM event has occurred');
break;
}
}
//callback fired when notification received in iOS
function onNotificationAPN (event)
{
if ( event.alert )
{
//do something with the push message. This function is fired when push message is received or if user clicks on the tile.
alert(event.alert);
}
if ( event.sound )
{
//play notification sound. Ignore when app is in foreground.
var snd = new Media(event.sound);
snd.play();
}
if ( event.badge )
{
//change app icon badge number. If app is in foreground ignore it.
window.plugins.pushNotification.setApplicationIconBadgeNumber(successHandler, errorHandler, event.badge);
}
}
All I get is an alert that says "result = ok". The alerts later on in the code don't happen. I've tried making sense of the code but I'm not getting anywhere. Any suggestions? Is there a tutorial for this I'm not finding?
Those legacy intel.xdk functions are being retired (the will continue to live in an 01.org, see the notice on this page: https://software.intel.com/en-us/node/492826).
I recommend you investigate one of the many push notification Cordova plugins that are available. Use your favorite web search tool to search for something like "cordova phonegap push notification plugin" to find some. The good ones will have examples of how to use.
Note:-
Unregister - Its not strictly necessary to call it.....
Ensure that you have a sender ID for Android (no idea about iOS).
Result OK means that the plugin is installed correctly and has run properly.
Problems could be due to:
Incorrect sender ID
Testing in emulator without adequate setup
Important - Push notifications are intended for real devices. They are not tested for WP8 Emulator. The registration process will fail on the iOS simulator. Notifications can be made to work on the Android Emulator, however doing so requires installation of some helper libraries, as outlined here, under the section titled "Installing helper libraries and setting up the Emulator".
onNotification must be available as a global object. So try attaching it to the window. Refer to this question
Examples of properly initializing PushPlugin in:
Ionic (my answer)