Udostępnij za pośrednictwem


Powiadomienia lokalne

Przeglądaj przykład. Przeglądanie przykładu

Powiadomienia lokalne to alerty wysyłane przez aplikacje zainstalowane na urządzeniu. Powiadomienia lokalne są często używane w przypadku takich funkcji jak:

  • Zdarzenia kalendarza
  • Przypomnienia
  • Wyzwalacze oparte na lokalizacji

Każda platforma wymaga własnej implementacji kodu natywnego do wysyłania i odbierania powiadomień lokalnych. Jednak każda implementacja platformy może być abstrakcyjna w warstwie międzyplatformowej, dzięki czemu istnieje spójny interfejs API do wysyłania i odbierania powiadomień lokalnych w aplikacji interfejsu użytkownika aplikacji wieloplatformowej platformy .NET (.NET MAUI).

Tworzenie abstrakcji międzyplatformowej

Aplikacja .NET MAUI powinna wysyłać i odbierać powiadomienia lokalne bez obaw o implementacje platformy bazowej. Następujący INotificationManagerService interfejs służy do definiowania międzyplatformowego interfejsu API, który może służyć do interakcji z powiadomieniami lokalnymi:

public interface INotificationManagerService
{
    event EventHandler NotificationReceived;
    void SendNotification(string title, string message, DateTime? notifyTime = null);
    void ReceiveNotification(string title, string message);
}

Ten interfejs definiuje następujące operacje:

  • Zdarzenie NotificationReceived umożliwia aplikacji obsługę powiadomień przychodzących.
  • Metoda SendNotification wysyła powiadomienie w opcjonalnym DateTimeobiekcie .
  • Metoda ReceiveNotification przetwarza powiadomienie po odebraniu przez platformę podstawową.

Interfejs należy zaimplementować na każdej platformie, która ma obsługiwać powiadomienia lokalne.

Implementowanie powiadomień lokalnych w systemie Android

W systemie Android lokalne powiadomienie jest komunikatem wyświetlanym poza interfejsem użytkownika aplikacji w celu dostarczenia przypomnień lub innych informacji z aplikacji. Użytkownicy mogą nacisnąć powiadomienie, aby otworzyć aplikację lub opcjonalnie podjąć akcję bezpośrednio z powiadomienia. Aby uzyskać informacje o powiadomieniach lokalnych w systemie Android, zobacz Omówienie powiadomień w developer.android.com.

Aby aplikacja .NET MAUI wysyłała i odbierała powiadomienia w systemie Android, aplikacja musi zapewnić implementację interfejsu INotificationManagerService .

Wysyłanie i odbieranie powiadomień lokalnych

W systemie Android NotificationManagerService klasa implementuje INotificationManagerService interfejs i zawiera logikę wysyłania i odbierania powiadomień lokalnych:

using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;

namespace LocalNotificationsDemo.Platforms.Android;

public class NotificationManagerService : INotificationManagerService
{
    const string channelId = "default";
    const string channelName = "Default";
    const string channelDescription = "The default channel for notifications.";

    public const string TitleKey = "title";
    public const string MessageKey = "message";

    bool channelInitialized = false;
    int messageId = 0;
    int pendingIntentId = 0;

    NotificationManagerCompat compatManager;

    public event EventHandler NotificationReceived;

    public static NotificationManagerService Instance { get; private set; }

    public NotificationManagerService()
    {
        if (Instance == null)
        {
            CreateNotificationChannel();
            compatManager = NotificationManagerCompat.From(Platform.AppContext);
            Instance = this;
        }
    }

    public void SendNotification(string title, string message, DateTime? notifyTime = null)
    {
        if (!channelInitialized)
        {
            CreateNotificationChannel();
        }

        if (notifyTime != null)
        {
            Intent intent = new Intent(Platform.AppContext, typeof(AlarmHandler));
            intent.PutExtra(TitleKey, title);
            intent.PutExtra(MessageKey, message);
            intent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);

            var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
                ? PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable
                : PendingIntentFlags.CancelCurrent;

            PendingIntent pendingIntent = PendingIntent.GetBroadcast(Platform.AppContext, pendingIntentId++, intent, pendingIntentFlags);
            long triggerTime = GetNotifyTime(notifyTime.Value);
            AlarmManager alarmManager = Platform.AppContext.GetSystemService(Context.AlarmService) as AlarmManager;
            alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent);
        }
        else
        {
            Show(title, message);
        }
    }

    public void ReceiveNotification(string title, string message)
    {
        var args = new NotificationEventArgs()
        {
            Title = title,
            Message = message,
        };
        NotificationReceived?.Invoke(null, args);
    }

    public void Show(string title, string message)
    {
        Intent intent = new Intent(Platform.AppContext, typeof(MainActivity));
        intent.PutExtra(TitleKey, title);
        intent.PutExtra(MessageKey, message);
        intent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);

        var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
            ? PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable
            : PendingIntentFlags.UpdateCurrent;

        PendingIntent pendingIntent = PendingIntent.GetActivity(Platform.AppContext, pendingIntentId++, intent, pendingIntentFlags);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(Platform.AppContext, channelId)
            .SetContentIntent(pendingIntent)
            .SetContentTitle(title)
            .SetContentText(message)
            .SetLargeIcon(BitmapFactory.DecodeResource(Platform.AppContext.Resources, Resource.Drawable.dotnet_logo))
            .SetSmallIcon(Resource.Drawable.message_small);

        Notification notification = builder.Build();
        compatManager.Notify(messageId++, notification);  
    }

    void CreateNotificationChannel()
    {
        // Create the notification channel, but only on API 26+.
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            var channelNameJava = new Java.Lang.String(channelName);
            var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
            {
                Description = channelDescription
            };
            // Register the channel
            NotificationManager manager = (NotificationManager)Platform.AppContext.GetSystemService(Context.NotificationService);
            manager.CreateNotificationChannel(channel);
            channelInitialized = true;
        }
    }

    long GetNotifyTime(DateTime notifyTime)
    {
        DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime);
        double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds;
        long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000;
        return utcAlarmTime; // milliseconds
    }
}

Klasa NotificationManagerService powinna zostać umieszczona w folderze Platformy > aplikacji dla systemu Android . Alternatywnie można wykonywać wielowersyjność na podstawie własnych kryteriów nazwy pliku i folderu, zamiast używać folderu Platformy > dla systemu Android . Aby uzyskać więcej informacji, zobacz Konfigurowanie wielu elementów docelowych.

System Android umożliwia aplikacjom definiowanie wielu kanałów dla powiadomień. Konstruktor NotificationManagerService tworzy podstawowy kanał używany do wysyłania powiadomień. Metoda SendNotification definiuje logikę specyficzną dla platformy wymaganą do utworzenia i wysłania powiadomienia. Metoda jest wywoływana ReceiveNotification przez system operacyjny Android po odebraniu komunikatu i wywołuje procedurę NotificationReceived obsługi zdarzeń. Aby uzyskać więcej informacji, zobacz Tworzenie powiadomienia w developer.android.com.

Metoda SendNotification tworzy powiadomienie lokalne natychmiast lub dokładnie DateTime. Powiadomienie można zaplanować dla dokładnego DateTimeAlarmManager użycia klasy, a powiadomienie zostanie odebrane przez obiekt pochodzący z BroadcastReceiver klasy:

[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        if (intent?.Extras != null)
        {
            string title = intent.GetStringExtra(NotificationManagerService.TitleKey);
            string message = intent.GetStringExtra(NotificationManagerService.MessageKey);

            NotificationManagerService manager = NotificationManagerService.Instance ?? new NotificationManagerService();
            manager.Show(title, message);
        }
    }
}

Ważne

Domyślnie powiadomienia zaplanowane przy użyciu klasy nie przetrwają ponownego AlarmManager uruchomienia urządzenia. Możesz jednak zaprojektować aplikację, aby automatycznie ponownie zaplanować powiadomienia, jeśli urządzenie zostanie uruchomione ponownie. Aby uzyskać więcej informacji, zobacz Uruchamianie alarmu po ponownym uruchomieniu urządzenia w harmonogramie powtarzających się alarmów na developer.android.com. Aby uzyskać informacje na temat przetwarzania w tle w systemie Android, zobacz Przewodnik po przetwarzaniu w tle w developer.android.com.

Obsługa powiadomień przychodzących

Aplikacja systemu Android musi wykrywać przychodzące powiadomienia i powiadamiać NotificationManagerService wystąpienie. Jednym ze sposobów osiągnięcia tego celu jest zmodyfikowanie MainActivity klasy.

Atrybut Activity klasy MainActivity powinien określać LaunchMode wartość LaunchMode.SingleTop:

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, //... )]
public class MainActivity : MauiAppCompatActivity
{
}

Tryb SingleTop uniemożliwia uruchamianie wielu wystąpień obiektu Activity , gdy aplikacja znajduje się na pierwszym planie. Może to LaunchMode nie być odpowiednie w przypadku aplikacji, które uruchamiają wiele działań w bardziej złożonych scenariuszach powiadomień. Aby uzyskać więcej informacji na temat LaunchMode wartości wyliczenia, zobacz Android Activity LaunchMode on developer.android.com (Android Activity LaunchMode on developer.android.com).

Należy MainActivity również zmodyfikować klasę w celu odbierania powiadomień przychodzących:

public class MainActivity : MauiAppCompatActivity
{
    protected override void OnCreate(Bundle? savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        CreateNotificationFromIntent(Intent);
    }

    protected override void OnNewIntent(Intent? intent)
    {
        base.OnNewIntent(intent);

        CreateNotificationFromIntent(intent);
    }

    static void CreateNotificationFromIntent(Intent intent)
    {
        if (intent?.Extras != null)
        {
            string title = intent.GetStringExtra(LocalNotificationsDemo.Platforms.Android.NotificationManagerService.TitleKey);
            string message = intent.GetStringExtra(LocalNotificationsDemo.Platforms.Android.NotificationManagerService.MessageKey);

            var service = IPlatformApplication.Current.Services.GetService<INotificationManagerService>();
            service.ReceiveNotification(title, message);
        }
    }
}

Metoda CreateNotificationFromIntent wyodrębnia dane powiadomień z argumentu intent i przekazuje je do ReceiveNotification metody w NotificationManagerService klasie . Metoda CreateNotificationFromIntent jest wywoływana zarówno z metody, jak OnCreate i OnNewIntent metody:

  • Gdy aplikacja zostanie uruchomiona przez dane powiadomień, Intent dane zostaną przekazane do OnCreate metody .
  • Jeśli aplikacja znajduje się już na pierwszym planie, Intent dane zostaną przekazane do OnNewIntent metody .

Sprawdzanie uprawnień

System Android 13 (interfejs API 33) i nowsze wersje wymagają POST_NOTIFICATIONS uprawnienia do wysyłania powiadomień z aplikacji. To uprawnienie powinno zostać zadeklarowane w pliku AndroidManifest.xml :

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Aby uzyskać więcej informacji o uprawnieniach POST_NOTIFICATIONS , zobacz Uprawnienia środowiska uruchomieniowego powiadomień w developer.android.com.

Należy zaimplementować klasę uprawnień, która sprawdza deklarację uprawnień w czasie wykonywania:

using Android;

namespace LocalNotificationsDemo.Platforms.Android;

public class NotificationPermission : Permissions.BasePlatformPermission
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions
    {
        get
        {
            var result = new List<(string androidPermission, bool isRuntime)>();
            if (OperatingSystem.IsAndroidVersionAtLeast(33))
                result.Add((Manifest.Permission.PostNotifications, true));
            return result.ToArray();
        }
    }
}

Po uruchomieniu aplikacji należy poprosić użytkownika o udzielenie tego uprawnienia przed podjęciem próby wysłania powiadomienia lokalnego:

PermissionStatus status = await Permissions.RequestAsync<NotificationPermission>();

Aby uzyskać więcej informacji na temat uprawnień programu .NET MAUI, zobacz Uprawnienia.

Implementowanie powiadomień lokalnych w systemach iOS i Mac Catalyst

Na platformach firmy Apple powiadomienie lokalne jest komunikatem, który przekazuje ważne informacje użytkownikom. System obsługuje dostarczanie powiadomień na podstawie określonego czasu lub lokalizacji. Aby uzyskać informacje o powiadomieniach lokalnych na platformach firmy Apple, zobacz Planowanie powiadomienia lokalnie z aplikacji na developer.apple.com.

Aby aplikacja .NET MAUI wysyłała i odbierała powiadomienia na platformach firmy Apple, aplikacja musi zapewnić implementację interfejsu INotificationManagerService .

Wysyłanie i odbieranie powiadomień lokalnych

Na platformach NotificationManagerService firmy Apple klasa implementuje INotificationManagerService interfejs i zawiera logikę wysyłania i odbierania powiadomień lokalnych:

using Foundation;
using UserNotifications;

namespace LocalNotificationsDemo.Platforms.iOS;

public class NotificationManagerService : INotificationManagerService
{
    int messageId = 0;
    bool hasNotificationsPermission;

    public event EventHandler? NotificationReceived;

    public NotificationManagerService()
    {
        // Create a UNUserNotificationCenterDelegate to handle incoming messages.
        UNUserNotificationCenter.Current.Delegate = new NotificationReceiver();

        // Request permission to use local notifications.
        UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert, (approved, err) =>
        {
            hasNotificationsPermission = approved;
        });
    }

    public void SendNotification(string title, string message, DateTime? notifyTime = null)
    {
        // App doesn't have permissions.
        if (!hasNotificationsPermission)
            return;

        messageId++;
        var content = new UNMutableNotificationContent()
        {
            Title = title,
            Subtitle = "",
            Body = message,
            Badge = 1
        };

        UNNotificationTrigger trigger;
        if (notifyTime != null)
            // Create a calendar-based trigger.
            trigger = UNCalendarNotificationTrigger.CreateTrigger(GetNSDateComponents(notifyTime.Value), false);
        else
            // Create a time-based trigger, interval is in seconds and must be greater than 0.
            trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false);

        var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger);
        UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
        {
            if (err != null)
                throw new Exception($"Failed to schedule notification: {err}");
        });
    }

    public void ReceiveNotification(string title, string message)
    {
        var args = new NotificationEventArgs()
        {
            Title = title,
            Message = message
        };
        NotificationReceived?.Invoke(null, args);
    }

    NSDateComponents GetNSDateComponents(DateTime dateTime)
    {
        return new NSDateComponents
        {
            Month = dateTime.Month,
            Day = dateTime.Day,
            Year = dateTime.Year,
            Hour = dateTime.Hour,
            Minute = dateTime.Minute,
            Second = dateTime.Second
        };
    }
}

Klasa NotificationManagerService powinna zostać umieszczona w folderze Platformy > aplikacji dla systemu iOS lub Platformy > Mac Catalyst . Alternatywnie można wykonywać wielowersyjność na podstawie własnych kryteriów nazwy pliku i folderu, zamiast używać folderów Platformy . Aby uzyskać więcej informacji, zobacz Konfigurowanie wielu elementów docelowych.

Na platformach firmy Apple należy zażądać uprawnień do korzystania z powiadomień przed podjęciem próby zaplanowana powiadomienia. Dzieje się tak w konstruktorze NotificationManagerService . Aby uzyskać więcej informacji na temat uprawnień powiadomień lokalnych, zobacz Monituj o uprawnienie do korzystania z powiadomień w developer.apple.com.

Metoda SendNotification definiuje logikę wymaganą do utworzenia i wysłania powiadomienia oraz tworzy natychmiastowe powiadomienie lokalne przy użyciu obiektu lub dokładnie UNTimeIntervalNotificationTrigger przy użyciu DateTimeUNCalendarNotificationTrigger obiektu. Metoda jest wywoływana ReceiveNotification przez system iOS po odebraniu komunikatu i wywołuje procedurę obsługi zdarzeń NotificationReceived .

Obsługa powiadomień przychodzących

Na platformach firmy Apple, aby obsługiwać komunikaty przychodzące, należy utworzyć delegata, który podklasy UNUserNotificationCenterDelegate:

using UserNotifications;

namespace LocalNotificationsDemo.Platforms.iOS;

public class NotificationReceiver : UNUserNotificationCenterDelegate
{
    // Called if app is in the foreground.
    public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
    {
        ProcessNotification(notification);

        var presentationOptions = (OperatingSystem.IsIOSVersionAtLeast(14))
            ? UNNotificationPresentationOptions.Banner
            : UNNotificationPresentationOptions.Alert;

        completionHandler(presentationOptions);
    }

    // Called if app is in the background, or killed state.
    public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
    {
        if (response.IsDefaultAction)
            ProcessNotification(response.Notification);

        completionHandler();
    }

    void ProcessNotification(UNNotification notification)
    {
        string title = notification.Request.Content.Title;
        string message = notification.Request.Content.Body;

        var service = IPlatformApplication.Current?.Services.GetService<INotificationManagerService>();
        service?.ReceiveNotification(title, message);
    }
}

Klasa NotificationReceiver jest zarejestrowana jako UNUserNotificationCenter delegat w konstruktorze NotificationManagerService i dostarcza przychodzące dane powiadomień do ReceiveNotification metody w NotificationManagerService klasie.

Implementowanie powiadomień lokalnych w systemie Windows

Powiadomienia lokalne w zestawie SDK aplikacji systemu Windows to komunikaty, które aplikacja może wysyłać do użytkownika, gdy nie znajdują się one obecnie w aplikacji. Zawartość powiadomienia jest wyświetlana w przejściowym oknie w prawym dolnym rogu ekranu i w Centrum powiadomień. Powiadomienia lokalne mogą służyć do informowania użytkownika o stanie aplikacji lub monitowania użytkownika o podjęcie akcji.

Aby uzyskać informacje o powiadomieniach lokalnych w systemie Windows, w tym szczegóły implementacji dla spakowanych i rozpakowanych aplikacji, zobacz Omówienie powiadomień aplikacji.

Ostrzeżenie

Obecnie zaplanowane powiadomienia nie są obsługiwane w Windows App SDK. Aby uzyskać więcej informacji, zobacz Wniosek o funkcję: Harmonogram powiadomień typu toast.

Rejestrowanie implementacji platformy

Każda implementacja NotificationManagerService powinna być zarejestrowana względem interfejsu INotificationManagerService , aby operacje udostępniane przez interfejs mogły być wywoływane z kodu międzyplatformowego. Można to osiągnąć, rejestrując typy za pomocą Services właściwości MauiAppBuilder obiektu w metodzie CreateMauiAppMauiProgram w klasie:

#if ANDROID
            builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.Android.NotificationManagerService>();
#elif IOS
            builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.iOS.NotificationManagerService>();
#elif MACCATALYST
            builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.MacCatalyst.NotificationManagerService>();
#elif WINDOWS
            builder.Services.AddTransient<INotificationManagerService, LocalNotificationsDemo.Platforms.Windows.NotificationManagerService>();          
#endif

Aby uzyskać więcej informacji na temat wstrzykiwania zależności w programie .NET MAUI, zobacz Wstrzykiwanie zależności.

Wysyłanie i odbieranie powiadomień lokalnych

Implementację INotificationManagerService można rozwiązać za pomocą automatycznego rozwiązywania zależności lub jawnego rozwiązywania zależności. W poniższym przykładzie pokazano użycie jawnego rozpoznawania INotificationManagerService zależności w celu rozwiązania implementacji:

// Assume the app uses a single window.
INotificationManagerService notificationManager =
    Application.Current?.Windows[0].Page?.Handler?.MauiContext?.Services.GetService<INotificationManagerService>();

Aby uzyskać więcej informacji na temat rozpoznawania zarejestrowanych typów, zobacz Rozwiązanie.

Po rozwiązaniu INotificationManagerService implementacji można wywołać jej operacje:

// Send
notificationManager.SendNotification();

// Scheduled send
notificationManager.SendNotification("Notification title goes here", "Notification messages goes here.", DateTime.Now.AddSeconds(10));

// Receive
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
    var eventData = (NotificationEventArgs)eventArgs;

    MainThread.BeginInvokeOnMainThread(() =>
    {
        // Take required action in the app once the notification has been received.
    });
};
}

Procedura NotificationReceived obsługi zdarzeń rzutuje argumenty zdarzeń na NotificationEventArgs, które definiują Title właściwości i Message :

public class NotificationEventArgs : EventArgs
{
    public string Title { get; set; }
    public string Message { get; set; }
}

W systemie Android po wysłaniu powiadomienia zostanie wyświetlone jako ikona na pasku stanu:

Ikona powiadomienia na pasku stanu w systemie Android.

Po przesunięciu w dół na pasku stanu zostanie otwarty szuflada powiadomień:

Lokalne powiadomienie w szufladzie powiadomień w systemie Android.

Naciśnięcie powiadomienia uruchamia aplikację. Powiadomienie pozostaje widoczne w szufladzie powiadomień, dopóki nie zostanie ono odrzucone przez aplikację lub użytkownika.

W systemie iOS powiadomienia przychodzące są automatycznie odbierane przez aplikację bez konieczności wprowadzania danych przez użytkownika:

Powiadomienie w systemie iOS.

W usłudze Mac Catalyst powiadomienia przychodzące są automatycznie odbierane przez Centrum powiadomień:

Powiadomienie w Centrum powiadomień w systemie macOS.

Aby uzyskać więcej informacji na temat Centrum powiadomień, zobacz Use Notification Centre on Mac on Mac on support.apple.com (Korzystanie z centrum powiadomień na komputerze Mac w support.apple.com).