0

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?

  1. When app is foregrounded on any of my two testing phones (Oreo, Pie) and I send notification, it arrives always.
  2. Message usually doesn't arrive after new build when app is switched off.
  3. 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.
  4. Messages absolutely don't arrive to my users even if they are foregrounded.
  5. 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...

hoacin
  • 340
  • 1
  • 2
  • 19
  • You do update the firebase token of the device you are trying to send the notification to every time it gets refreshed right? (after app reinstall, clear cache etc) Also, when it does fail(not get delivered), what response do you get from your call to firebase send endpoint? Check both the status code and the body of the response. – Derryl Thomas Jun 27 '19 at 09:27
  • @DerrylThomas By response you mean what do I get on line 'String sResponseFromServer = tReader.ReadToEnd()' in my SendDataMessage function?To token refreshing, I actually don't know what am I exactly supposed to do with the token and what exactly its refreshing means. I have code scratch with "com.google.firebase.INSTANCE_ID_EVENT" service that I sometimes used and sometimes not. Is it relevant and is the code right? To me all things regarding token seemed relevant only when message is directed to concrete user. Is it relevant also in messaging to topics? – hoacin Jun 27 '19 at 12:54
  • I have not worked with C# and am not sure about the syntax and objects used. By response, I mean the HTTP response you get when you call the `https://fcm.googleapis.com/fcm/send` endpoint. The token, in short, is what identifies your app in your device and sends your notification to it. There are situations where this token gets refreshed(see [link](https://firebase.google.com/docs/cloud-messaging/android/client#sample-register)). In such situations, you have to send your notification to the new token. The old one will not be reachable anymore. – Derryl Thomas Jun 28 '19 at 04:54
  • This is how it works when sent to individual devices, I am not sure about topics. Haven't tried it out. Sorry. – Derryl Thomas Jun 28 '19 at 04:54
  • Sending side is fine I guess, shows message ID and some big number. – hoacin Jun 28 '19 at 05:52

0 Answers0