Hangs when notification tap brings NET MAUI Blazor app to foreground

97 Views Asked by At

I'm trying to implement Firebase Messaging (by Plugin.Firebase nuget) for an NET MAUI Blazor app.

I've managed to send and receive notifications from FCM console, but when my app is minimized, the notification tap restores it but with purple background only (default in MAUI app).

This only happens when there is a BlazorWebView in MainPage.xml, I tried the same with only a label above it, and the label appears fine when there's no BlazorWebView.

However, it seems no breakpoints in my Razor files get hit, so I don't know what could be the culprit, the only code that gets executed on notification tap is MainAcitivity.OnCreate which calls base.OnCreate which executes MauiProgram.RegisterFirebaseServices and crashes.

I've stripped down all my Razor pages to bare minimum, but no breakpoints are hit, so even though the only way the app crashes is with BlazorWebView, I'm not sure where to look. The bones for NET MAUI Firebase implementation were taken from: https://github.com/coop-tim/maui-sample

MainActivity.cs:

using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using Microsoft.Extensions.Configuration;
using Plugin.Firebase.CloudMessaging;

namespace MauiSample;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
    public static bool AppCenterConfigured { get; set; }

    protected override void OnCreate(Bundle? savedInstanceState)
    {
        try
        {
            var intent = this.Intent;

            HandleIntent(Intent);

            CreateNotificationChannelIfNeeded();

            base.OnCreate(savedInstanceState);

        }

        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    protected override void OnNewIntent(Intent? intent)
    {
        try
        {
            base.OnNewIntent(intent);
            HandleIntent(intent);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    private static void HandleIntent(Intent? intent)
    {
        try
        {
            if (intent is not null)
            {
                FirebaseCloudMessagingImplementation.OnNewIntent(intent);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
    {
        try
        {
            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
#if ANDROID23_0_OR_GREATER
#pragma warning disable CA1416 // Validate platform compatibility
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
#pragma warning restore CA1416 // Validate platform compatibility
#endif
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    private void CreateNotificationChannelIfNeeded()
    {
        try
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
            {
                if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.PostNotifications) != Permission.Granted)
                {
                    ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.PostNotifications }, 0);
                }
            }

            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                CreateNotificationChannel();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    private void CreateNotificationChannel()
    {
        try
        {
            var channelId = $"{PackageName}.general";
#if ANDROID26_0_OR_GREATER
            var notificationManager = GetSystemService(NotificationService) as NotificationManager;
            if (notificationManager is not null)
            {
                if ((int)Build.VERSION.SdkInt > 26)
                {
#pragma warning disable CA1416 // Validate platform compatibility
                    var channel = new NotificationChannel(channelId, "General", NotificationImportance.Default);
                    notificationManager.CreateNotificationChannel(channel);
#pragma warning restore CA1416 // Validate platform compatibility
                }
            }

            FirebaseCloudMessagingImplementation.ChannelId = channelId;
#endif
            //FirebaseCloudMessagingImplementation.SmallIconRef = Resource.Drawable.ic_push_small;
        }
        catch(Exception e)
        {
            Console.WriteLine(e.ToString());
        }


    }
}

MauiProgram.cs:

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Logging;
using WaybackMaui.Authentication;
using Radzen;
using Microsoft.Kiota.Abstractions.Authentication;
using Shared.Settings;
using Microsoft.Extensions.Configuration;
using System.Reflection;
using Microsoft.Maui.LifecycleEvents;
using Plugin.Firebase.CloudMessaging;
using Plugin.Firebase.Analytics;
using Wayback.ViewModels;
using Serilog;
using Plugin.Firebase.CloudMessaging.EventArgs;
using WaybackMaui.Services;



#if IOS
using Plugin.Firebase.Core.Platforms.iOS;
#elif ANDROID
using Plugin.Firebase.Core.Platforms.Android;
#endif

namespace WaybackMaui
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug() // Set the minimum log level
            .WriteTo.Console() // Add console sink
            .WriteTo.AndroidLog()
            .CreateLogger();

            Log.Logger.Debug("proba konzole");

            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .RegisterFirebaseServices()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                });

            // Use Serilog for logging
            builder.Logging.ClearProviders(); // Clear default logging providers
            builder.Logging.AddSerilog(); // Use Serilog

            builder.Services.AddAuthorizationCore();
            builder.Services.AddTransient<CustomAuthenticationStateProvider>();
            builder.Services.AddTransient<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthenticationStateProvider>());
            builder.Services.AddTransient<DialogService>();
            builder.Services.AddTransient<IAccessTokenProvider, AccessTokenProvider>();
            builder.Services.AddTransient<JwtTokenAuthenticationProvider>();
            builder.Services.AddTransient<IJwtTokenAuthenticationProvider, JwtTokenAuthenticationProvider>();
            builder.Services.AddTransient<ICustomAuthenticationStateProvider, CustomAuthenticationStateProvider>();
            builder.Services.AddTransient<IFirebaseService, FirebaseService>();
            builder.Services.AddTransient<FirebaseService>();
            //builder.Services.AddSingleton<IFirebaseTokenRetriever, FirebaseTokenRetriever>();            

            var assembly = Assembly.GetExecutingAssembly();
            var environment = GetCurrentEnvironment();
            var fileName = $"{assembly.GetName().Name}.appsettings.{environment}.json";

            using var stream = assembly.GetManifestResourceStream(fileName);

            var configBuilder = new ConfigurationBuilder()
                .AddJsonStream(stream);
            var configuration = configBuilder.Build();

            // Load settings into ApiSettings object
            var apiSettings = configuration.GetSection("ApiSettings").Get<ApiSettings>();

            if (apiSettings == null)
            {
                throw new Exception("Api Settings not available");
            }

            // Register ApiSettings instance with DI
            builder.Services.AddSingleton(apiSettings);
            builder.Services.AddMauiBlazorWebView();            

            //builder.Services.AddMudServices();
            builder.Services.AddRadzenComponents();
            //builder.Services.AddTransient<MainPageViewModel>();
            //builder.Services.AddTransient<MainPage>();            

#if DEBUG
            builder.Services.AddBlazorWebViewDeveloperTools();
            builder.Logging.AddDebug();
#endif

            //builder.Services            
            //.AddViews()
            //.AddViewModels()
            //.AddCustomServices(config);

            return builder.Build();
        }

        private static string GetCurrentEnvironment()
        {
            #if DEBUG
            return "Development";
            #else
            return "Production";
            #endif
        }

        private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder)
        {
            try
            {
                builder.ConfigureLifecycleEvents(events => {
#if IOS
                    events.AddiOS(iOS => iOS.WillFinishLaunching((app, launchOptions) => {
                        CrossFirebase.Initialize();
                        FirebaseCloudMessagingImplementation.Initialize();
                        return true;
                    }));
#elif ANDROID
                    events.AddAndroid(android => android.OnCreate((activity, _) =>
                    {
                        CrossFirebase.Initialize(activity);                        
                        //FirebaseAnalyticsImplementation.Initialize(activity);
                    }));
#endif
                });

                //CrossFirebaseCloudMessaging.Current.NotificationTapped += Current_NotificationTapped;
                CrossFirebaseCloudMessaging.Current.TokenChanged += FcmTokenChanged;


                return builder;
            }

            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return builder;
            }
            
        }

        private static void FcmTokenChanged(object? sender, FCMTokenChangedEventArgs e)
        {

        }

        private static void Current_NotificationTapped(object? sender, FCMNotificationTappedEventArgs e)
        {
            try
            {
                var notificationData = e?.Notification?.Data;
                if (notificationData != null && notificationData.ContainsKey("link"))
                {
                    //Do something with the link
                }
            }
            catch(Exception exc) { 
                Console.WriteLine(exc.ToString());
            }
            
        }
    }

    
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.myapp.myapp" android:versionCode="1">
    <application android:allowBackup="true" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" android:label="myapp">
        <meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="true" />
        <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="com.myapp.myapp" />
            </intent-filter>
        </receiver>
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="34" />
    <uses-permission android:name="com.google.android.gms.permission.AD_ID" />
</manifest>
0

There are 0 best solutions below