I decided to write all the relevant code here as full working c# example is something what I am really badly missing and I don't know where to search for the problem. I am completely stuck.
I tried so many combinations, commenting and uncommenting parts of code, but I simply don't understand how to set things up. I no longer see any logic in what am I doing. It's just desparate copypasting...
Down here is current state of my code:
This is my manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.alienchess.android" android:versionCode="87" android:versionName="Patrol on the Milky Way (87)" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.vending.BILLING" />
<application android:label="Alien Chess" android:icon="@drawable/Alien">
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.alienchess.android.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
</provider>
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
</application>
</manifest>
I have neither messaging service nor Instance ID service defined here. I tried that too, copypasted code from this question but as written above, I no longer know what helps the app. I can't just fill my code with tone of code I don't understand.
This is file with my messagine service
using ...
namespace AlienChessAndroid
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
private async void SendNotification(RemoteMessage remoteMessage)
{
...
notificationBuilder = new NotificationCompat.Builder(this)
.SetContentTitle(title)
.SetAutoCancel(true)
.SetSmallIcon(ali)
.SetSound(defaultSoundUri)
.SetCategory(systemCategory)
.SetPriority(notificationPriority)
.SetContentIntent(contentIntent);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
notificationBuilder.SetChannelId(channel);
...
var notificationManager = NotificationManager.FromContext(this);
Notification notification = notificationBuilder.Build();
notificationManager.Notify(0, notification);
}
public override void OnMessageReceived(RemoteMessage remoteMessage)
{
//base.OnMessageReceived(remoteMessage);
SendNotification(remoteMessage);
}
}
}
SendNotification function works. I heavily tested also with just playing system sound. Under some circumstances the OnMessageReceived triggers correctly and then it works. I will write about it later.
Oreo Channels
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var notificationManager = NotificationManager.FromContext(LayoutBuilder.staticContext);
NotificationChannel channel = new NotificationChannel(RING_ID, "Starting soon",NotificationImportance.Default);
channel.Description="Last minute calls from topics inviting you to live streams.";
channel.SetShowBadge(false);
channel.EnableLights(true);
channel.LightColor = Color.DeepSkyBlue.ToArgb();
notificationManager.CreateNotificationChannel(channel);
...
}
I didn't forget to create Oreo notification channels. This is what I call on every app start up.
This is file with instance ID service
using...
namespace AlienChessAndroid
{
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
public override void OnTokenRefresh()
{
base.OnTokenRefresh();
}
}
}
To be honest I never found what is this used for in topic messaging and in more tests I didn't use this code. Seemed to me more like stuff for one on one messaging. Maybe I get it wrong.
SHA Certificates
I don't know if providing Firebase with SHA fingerprints is usefull for anything in respect to topic messaging but I today added debug, release and Play Store SHA1 fingerprints to Firebase Console too and updated google-services.json too. No change again...
How do I send data messages?
public static void SendDataMessage(MyNotification paramNotification)
{
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
//serverKey - Key from Firebase cloud messaging server
tRequest.Headers.Add(string.Format("Authorization: key={0}", LayoutBuilder.FCM_SECRET_SERVERKEY));
//Sender Id - From firebase project setting
tRequest.Headers.Add(string.Format("Sender: id={0}", LayoutBuilder.FCM_SECRET_SENDERID));
tRequest.ContentType = "application/json";
string strExpirationFileTime = DateTime.Now.AddMinutes(int.Parse(paramNotification.expires)).ToFileTime().ToString();
var payload = new
{
to = "/topics/" + paramNotification.topic,
data = new
{
sbody = paramNotification.shortBody,
lbody = paramNotification.longBody,
title = paramNotification.title,
channel = paramNotification.channel,
image = paramNotification.image.description,
link = paramNotification.link,
expires = strExpirationFileTime,
},
};
string postbody = JsonConvert.SerializeObject(payload).ToString();
Byte[] byteArray = Encoding.UTF8.GetBytes(postbody);
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
if (dataStreamResponse != null)
{
using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
//System.Diagnostics.Debug.WriteLine("Message response: " + sResponseFromServer);
}
}
}
}
}
This code is taken from question about sending push notification from c# and it works. I have there some expiration stuff which should be done differently but it isn't connected to the problem.
How do I subscribe?
Firebase.Messaging.FirebaseMessaging.Instance.SubscribeToTopic("internal");
I run this on every app startup. Function is called from non UI thread.
And finally, what's wrong?
- When app is foregrounded on any of my two testing phones (Oreo, Pie) and I send notification, it arrives always.
- Message usually doesn't arrive after new build when app is switched off.
- I observed pretty obvious pattern that once first message comes to any of my testing phones, then they keep coming under whatever circumstances, even if app is switched off for hours.
- Messages absolutely don't arrive to my users even if they are foregrounded.
- Once however message arrived also to device of one of my testers.
Numbers 4 and 5 are something I just don't get. I understand when it comes never or every single time, I can imagine high rate of success but message that almost never comes, that is something I don't understand...