-
Notifications
You must be signed in to change notification settings - Fork 782
docs: Add Firebase Cloud Messaging documentation #19718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mtmattei
wants to merge
3
commits into
master
Choose a base branch
from
docs/add-fcm-documentation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
--- | ||
Uno.Features.FirebaseCloudMessaging | ||
--- | ||
|
||
# Implementing Firebase Cloud Messaging (FCM) in Uno Platform Apps | ||
|
||
This guide walks through the process of implementing Firebase Cloud Messaging (FCM) for push notifications in an Uno Platform application targeting Android. | ||
|
||
## Step 1: Set Up Firebase Project | ||
|
||
1. Go to [Firebase Console](https://console.firebase.google.com/) | ||
2. Create a new project or use an existing one | ||
3. Add an Android app to your Firebase project: | ||
- Package name should match your application ID in the .csproj file (e.g., `com.companyname.UnoFCM`) | ||
- Download the `google-services.json` file | ||
|
||
## Step 2: Add Required NuGet Packages | ||
|
||
Add the following NuGet packages to your project, using the `<ItemGroup>` with a condition to ensure they're only added for the Android target: | ||
|
||
```xml | ||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0-android'"> | ||
<PackageReference Include="Xamarin.Firebase.Messaging" /> | ||
<PackageReference Include="Xamarin.GooglePlayServices.Base" /> | ||
<PackageReference Include="Xamarin.Google.Dagger" /> | ||
<PackageReference Include="Xamarin.AndroidX.Core" /> | ||
</ItemGroup> | ||
``` | ||
|
||
## Step 3: Add google-services.json to Your Project | ||
|
||
1. Place the downloaded `google-services.json` file in the `Platforms/Android` folder | ||
2. Add the following to your project file (.csproj): | ||
|
||
```xml | ||
<ItemGroup> | ||
<None Remove="Platforms\Android\google-services.json" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<GoogleServicesJson Include="Platforms\Android\google-services.json"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</GoogleServicesJson> | ||
</ItemGroup> | ||
``` | ||
|
||
## Step 4: Update AndroidManifest.xml | ||
|
||
Add the necessary permissions and receivers to your `AndroidManifest.xml` in the `Platforms/Android` folder: | ||
|
||
```xml | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<uses-permission android:name="android.permission.INTERNET" /> | ||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | ||
<uses-permission android:name="android.permission.WAKE_LOCK" /> | ||
|
||
<application> | ||
<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> | ||
</manifest> | ||
``` | ||
|
||
## Step 5: Create a Firebase Messaging Service | ||
|
||
Create a custom service class in the `Platforms/Android` directory called `MyFirebaseMessagingService.cs`: | ||
|
||
```csharp | ||
using Android.App; | ||
using Android.Content; | ||
using Firebase.Messaging; | ||
using AndroidX.Core.App; | ||
using Android.OS; | ||
using Android.Media; | ||
using Android.Graphics; | ||
|
||
namespace YourAppNamespace.Platforms.Android | ||
{ | ||
[Service(Exported = false)] | ||
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })] | ||
public class MyFirebaseMessagingService : FirebaseMessagingService | ||
{ | ||
public override void OnMessageReceived(RemoteMessage message) | ||
{ | ||
base.OnMessageReceived(message); | ||
var notificationBody = message.GetNotification()?.Body; | ||
var notificationTitle = message.GetNotification()?.Title; | ||
var imageUrl = message.GetNotification()?.ImageUrl?.ToString(); | ||
var dataPayload = message.Data; | ||
SendNotification(notificationTitle, notificationBody, imageUrl, dataPayload); | ||
} | ||
|
||
public override void OnNewToken(string token) | ||
{ | ||
base.OnNewToken(token); | ||
System.Diagnostics.Debug.WriteLine($"FCM Token: {token}"); | ||
// Store or send this token to your server | ||
} | ||
|
||
private async void SendNotification(string title, string body, string imageUrl, IDictionary<string, string> data) | ||
{ | ||
var notificationManager = (NotificationManager)GetSystemService(Context.NotificationService); | ||
var channelId = "DefaultChannel"; | ||
|
||
// Create notification channel for Android 8.0 (API level 26) and higher | ||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O) | ||
{ | ||
var channel = new NotificationChannel( | ||
channelId, | ||
"Default", | ||
NotificationImportance.Max) | ||
{ | ||
Description = "Default notifications channel" | ||
}; | ||
channel.SetShowBadge(true); | ||
channel.EnableLights(true); | ||
channel.EnableVibration(true); | ||
channel.SetBypassDnd(true); | ||
channel.LockscreenVisibility = NotificationVisibility.Public; | ||
notificationManager.CreateNotificationChannel(channel); | ||
} | ||
|
||
// Create intent that will open the app when notification is tapped | ||
var intent = PackageManager.GetLaunchIntentForPackage(PackageName); | ||
intent.AddFlags(ActivityFlags.ClearTop); | ||
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, | ||
PendingIntentFlags.OneShot | PendingIntentFlags.Immutable); | ||
|
||
// Build the notification | ||
var defaultSoundUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification); | ||
var notificationBuilder = new NotificationCompat.Builder(this, channelId) | ||
.SetSmallIcon(global::Android.Resource.Drawable.IcDialogAlert) // Replace with your own icon | ||
.SetContentTitle(title) | ||
.SetContentText(body) | ||
.SetAutoCancel(true) | ||
.SetSound(defaultSoundUri) | ||
.SetPriority(NotificationCompat.PriorityMax) | ||
.SetDefaults(NotificationCompat.DefaultAll) | ||
.SetVisibility(NotificationCompat.VisibilityPublic) | ||
.SetContentIntent(pendingIntent); | ||
|
||
// Add image if available | ||
if (!string.IsNullOrEmpty(imageUrl)) | ||
{ | ||
try | ||
{ | ||
using var client = new HttpClient(); | ||
var bitmap = await client.GetByteArrayAsync(imageUrl); | ||
var image = BitmapFactory.DecodeByteArray(bitmap, 0, bitmap.Length); | ||
notificationBuilder.SetLargeIcon(image) | ||
.SetStyle(new NotificationCompat.BigPictureStyle() | ||
.BigPicture(image) | ||
.BigLargeIcon((Bitmap)null)); | ||
} | ||
catch (Exception ex) | ||
{ | ||
System.Diagnostics.Debug.WriteLine($"Error loading image: {ex.Message}"); | ||
} | ||
} | ||
|
||
// Show the notification | ||
var notification = notificationBuilder.Build(); | ||
notificationManager.Notify(new Random().Next(), notification); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Step 6: Initialize Firebase in MainActivity | ||
|
||
Update your `MainActivity.Android.cs` to initialize Firebase and request notification permissions: | ||
|
||
```csharp | ||
using Android.App; | ||
using Android.OS; | ||
using Android.Content.PM; | ||
using Firebase; | ||
|
||
namespace YourAppNamespace.Platforms.Android | ||
{ | ||
[Activity(MainLauncher = true)] | ||
public class MainActivity : Microsoft.UI.Xaml.ApplicationActivity | ||
{ | ||
protected override void OnCreate(Bundle savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
|
||
// Initialize Firebase | ||
FirebaseApp.InitializeApp(this); | ||
|
||
// Request notification permissions for Android 13+ | ||
if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu) | ||
{ | ||
RequestNotificationPermission(); | ||
} | ||
} | ||
|
||
private void RequestNotificationPermission() | ||
{ | ||
if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu) | ||
{ | ||
this.RequestPermissions( | ||
new[] { global::Android.Manifest.Permission.PostNotifications }, | ||
1001); | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Step 7: Handling FCM Token | ||
|
||
To get and handle the FCM token for sending notifications to specific devices: | ||
|
||
1. The token is generated in the `OnNewToken` method of your Firebase Messaging Service | ||
1. You can log it for development purposes or send it to your server | ||
1. To manually retrieve the current token, you can add a method like this: | ||
|
||
```csharp | ||
public static async Task<string> GetFCMTokenAsync() | ||
{ | ||
var instanceId = FirebaseMessaging.Instance; | ||
var token = await instanceId.GetToken(); | ||
return token; | ||
} | ||
``` | ||
|
||
## Step 8: Testing Push Notifications | ||
|
||
You can test your implementation using the Firebase Console: | ||
|
||
1. Go to your Firebase project console | ||
1. Navigate to "Messaging" in the left sidebar | ||
1. Click "Create your first campaign" or "Send your first message" | ||
1. Choose "Test on Android" | ||
1. Add your FCM token (that was printed in the debug output) | ||
1. Fill in notification details and send | ||
|
||
## Advanced Usage | ||
|
||
### Handling Data Messages | ||
|
||
Data messages allow you to send custom data payloads. They are handled in the `OnMessageReceived` method: | ||
|
||
```csharp | ||
public override void OnMessageReceived(RemoteMessage message) | ||
{ | ||
base.OnMessageReceived(message); | ||
|
||
// Check if the message contains a data payload | ||
if (message.Data.Count > 0) | ||
{ | ||
// Handle the data payload | ||
var customData = message.Data; | ||
// Process the data as needed | ||
} | ||
|
||
// Check if the message contains a notification payload | ||
if (message.GetNotification() != null) | ||
{ | ||
var notificationBody = message.GetNotification().Body; | ||
var notificationTitle = message.GetNotification().Title; | ||
// Display notification | ||
} | ||
} | ||
``` | ||
|
||
### Handling Notification Taps | ||
|
||
To handle taps on notifications and perform specific actions: | ||
|
||
1. Add data to the pending intent: | ||
|
||
```csharp | ||
var intent = PackageManager.GetLaunchIntentForPackage(PackageName); | ||
intent.AddFlags(ActivityFlags.ClearTop); | ||
|
||
// Add data payload to the intent | ||
foreach (var item in data) | ||
{ | ||
intent.PutExtra(item.Key, item.Value); | ||
} | ||
|
||
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, | ||
PendingIntentFlags.OneShot | PendingIntentFlags.Immutable); | ||
``` | ||
|
||
2. In your MainActivity, handle the intent data: | ||
|
||
```csharp | ||
protected override void OnCreate(Bundle savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
FirebaseApp.InitializeApp(this); | ||
|
||
// Check if opened from notification | ||
if (Intent?.Extras != null) | ||
{ | ||
foreach (var key in Intent.Extras.KeySet()) | ||
{ | ||
var value = Intent.Extras.GetString(key); | ||
// Handle notification data | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Common Issues and Troubleshooting | ||
|
||
1. **Notifications not showing**: | ||
- Ensure you have correct permissions in the manifest | ||
- Check if notification channels are properly set up for Android 8.0+ | ||
- Verify FCM token is correctly received and used | ||
|
||
1. **Firebase initialization issues**: | ||
- Make sure `google-services.json` has the correct package name | ||
- Ensure Firebase is initialized before any Firebase-related operations | ||
|
||
1. **Permission issues on Android 13+**: | ||
- Make sure to explicitly request `POST_NOTIFICATIONS` permission | ||
|
||
1. **Token not generated**: | ||
- Ensure Google Play Services are up to date on the test device | ||
- Check internet connectivity | ||
|
||
## Resources | ||
|
||
- [Firebase Documentation](https://firebase.google.com/docs/cloud-messaging) | ||
- [Uno Platform Documentation](https://platform.uno/docs/) | ||
- [Android Notification Channels](https://developer.android.com/develop/ui/views/notifications/channels)\ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to add the UID section to the top of the file like we have in the other pages, example:
uno/doc/articles/contributing/guidelines/creating-tests.md
Lines 1 to 3 in 5ddf04c
Just needs to be some sort of relevant unique id for the page. Following the same pattern for other files in this directory, we should probably use something like
Uno.Features.FirebaseCloudMessaging