Sample Code for sending Firebase Cloud Message (FCM) with Xamarin
This blog show how to setup Firebase Cloud Message for Xamarin.Android
First, register project at console.firebase.google.com
Package name at firebase console
Make sure the package is your xamarin app's package ID, for example, com.yourcompany.yourapp
.
Also, make note at console.firebase.google.com for following values, because you'll need those when you add code at Xamarin.Android.
- Project ID
- Firebase Setting, General Tab.
- Web API Key
- Firebase Setting, General Tab
- Sender ID
- Firebase Setting, Cloud MessagingTab
Versions
First, following example code is under these version.
- Visual Studio 2015 - Version 14 Update 3
- Xamain - 4.4.0.34
- Xamarin.Android - 7.2.0.7
- Xamarin.iOS- 10.8.0.174
Add code to Xamarin.Android Code
packages.config at Xamarin.Android
We'll need firebase and google play service.
<package id="Xamarin.Firebase.Common" version="42.1021.0" targetFramework="monoandroid71" /> <package id="Xamarin.Firebase.Iid" version="42.1021.0" targetFramework="monoandroid71" /> <package id="Xamarin.Firebase.Messaging" version="42.1021.0" targetFramework="monoandroid71" /> <package id="Xamarin.Forms" version="2.3.4.224" targetFramework="monoandroid71" /> <package id="Xamarin.GooglePlayServices.Basement" version="42.1021.0" targetFramework="monoandroid71" /> <package id="Xamarin.GooglePlayServices.Tasks" version="42.1021.0" targetFramework="monoandroid71" />
AndroidManifest.xml
<application android:label="YourAppLabel" android:icon="@drawable/icon"> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <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> </application> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
google-services.json
Based on the current document, need google-services.json
under project. You could get this under console.firebase.google.com, however I'm still not sure why do we need this file. I'll do more research for this later.
Add code to MainActivity
private void FirebaseNotificationSetup() { var options = new FirebaseOptions.Builder() .SetApplicationId(GetConfigValues(ConfigKeys.FCMAndroidProjectID)) .SetApiKey(GetConfigValues(ConfigKeys.FCMAndroidAPIKEY)) .SetGcmSenderId(GetConfigValues(ConfigKeys.FCMAndroidSenderID)) .Build(); var firebaseApp = FirebaseApp.InitializeApp(ApplicationContext, options, APP_NAME); if (FirebaseApp.GetApps(this) != null) { var x = FirebaseApp.GetInstance(APP_NAME); } Task.Run(() => { var instanceID = FirebaseInstanceId.Instance; instanceID.DeleteInstanceId(); var iid1 = instanceID.Token; var iid2 = instanceID.GetToken(GetConfigValues(ConfigKeys.FCMAndroidSenderID), Firebase.Messaging.FirebaseMessaging.InstanceIdScope); // subscribe the topic as Test // this will be /topics/test FirebaseMessaging.Instance.SubscribeToTopic("Test"); }); } public enum ConfigKeys { FCMAndroidProjectID, FCMAndroidSenderID, FCMAndroidAPIKEY } public static string GetConfigValues(ConfigKeys keys) { switch (keys) { case ConfigKeys.FCMAndroidProjectID: return GetValue("YourProjectIdForDebug", "YourProjectIdForRelease"); case ConfigKeys.FCMAndroidSenderID: return GetValue("YourSenderIDForDebug", "YourSenderIDForRELEASE"); case ConfigKeys.FCMAndroidAPIKEY: return GetValue("YourWebAPIKEYForDebug", "YourWebAPIKEYForRELEASE"); default: return string.Empty; } } public static string GetValue(string debugValue, string releaseValue) { #if DEBUG return debugValue; #elif RELEASE return releaseValue; #else return string.Empty; #endif }
Code for refresh token
[Service] [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })] public class MyFirebaseIIDService : FirebaseInstanceIdService { const string TAG = "MyFirebaseIIDService"; public override void OnTokenRefresh() { var refreshedToken = FirebaseInstanceId.Instance.Token; SendRegistrationToServer(refreshedToken); } void SendRegistrationToServer(string token) { // Add custom implementation, as needed. } }
Code for receive message from app server
[Service] [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })] public class MyFirebaseMessagingService : FirebaseMessagingService { const string TAG = "MyFirebaseMsgService"; public override void OnMessageReceived(RemoteMessage message) { SendNotification(message.GetNotification().Body); } void SendNotification(string messageBody) { var intent = new Intent(this, typeof(MainActivity)); intent.AddFlags(ActivityFlags.ClearTop); var pendingIntent = PendingIntent.GetActivity(this, 0 /* Request code */, intent, PendingIntentFlags.OneShot); var defaultSoundUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification); var notificationBuilder = new NotificationCompat.Builder(this) .SetContentTitle("FCM Message") .SetContentText(messageBody) .SetAutoCancel(true) .SetSound(defaultSoundUri) .SetContentIntent(pendingIntent); var notificationManager = NotificationManager.FromContext(this); notificationManager.Notify(0, notificationBuilder.Build()); } }
App server code example
try { string applicationID = "firebase.google.com cloud message server key"; string senderId = "sender ID"; string deviceId = "device created by your device, emulator or actual device"; WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send"); tRequest.Method = "post"; tRequest.ContentType = "application/json"; var data = new { // send to device //to = deviceId, // send to subscribed topic "test" to = "/topics/Test", notification = new { body = "body", title = "my title", sound = "Enabled" } }; var json = JsonConvert.SerializeObject(data); Byte[] byteArray = Encoding.UTF8.GetBytes(json); tRequest.Headers.Add(string.Format("Authorization: key={0}", applicationID)); tRequest.Headers.Add(string.Format("Sender: id={0}", senderId)); 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()) { using (StreamReader tReader = new StreamReader(dataStreamResponse)) { String sResponseFromServer = tReader.ReadToEnd(); string str = sResponseFromServer; } } } } } catch (Exception ex) { string str = ex.Message; }
That's it
You could either use above app server code to send message or go to Firebase UI to send message to your device.
What's more
How to change project ID, sender ID for different Enviroment and since have to include google-services.json to the project?
Things for watch out
the topic should be match this format [a-zA-Z0-9-_.~%]{1,900}
, otherwise will get exceptin error.
{Java.Lang.IllegalArgumentException: Invalid topic name:
Reference
- Xamarin Document - remote nofitications with FCM
- Xamarin Document - firebase cloud messaging
- Article - How to Get Started With Push Notifications On Android
- Google document - Migrate a GCM Client App for Android to Firebase Cloud Messaging