Прочитать на английском

Поделиться через


Локальные уведомления

Выберите платформу

Просмотрите пример. Обзор примера

Локальные уведомления — это оповещения, отправляемые приложениями, установленными на устройстве. Локальные уведомления часто используются для таких функций, как:

  • События календаря
  • Напоминания
  • Триггеры на основе расположения

Для каждой платформы требуется собственная реализация собственного кода для отправки и получения локальных уведомлений. Однако каждая реализация платформы может быть абстрагирована на кроссплатформенный уровень, чтобы обеспечить согласованный API для отправки и получения локальных уведомлений в приложении многоплатформенного пользовательского интерфейса приложений .NET (.NET MAUI).

Создание кроссплатформенной абстракции

Приложение .NET MAUI должно отправлять и получать локальные уведомления, не беспокоясь о реализации базовой платформы. Следующий INotificationManagerService интерфейс используется для определения кроссплатформенного API, который можно использовать для взаимодействия с локальными уведомлениями:

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

Этот интерфейс определяет следующие операции:

  • Это NotificationReceived событие позволяет приложению обрабатывать входящие уведомления.
  • Метод SendNotification отправляет уведомление при необходимости DateTime.
  • Метод ReceiveNotification обрабатывает уведомление при получении базовой платформой.

Интерфейс должен быть реализован на каждой платформе, которую требуется поддерживать локальные уведомления.

Реализация локальных уведомлений в Android

В Android локальное уведомление — это сообщение, отображаемое за пределами пользовательского интерфейса приложения для предоставления напоминаний или других сведений из приложения. Пользователи могут касаться уведомления, чтобы открыть приложение, или при необходимости выполнить действие непосредственно из уведомления. Сведения о локальных уведомлениях в Android см. в обзоре уведомлений по developer.android.com.

Чтобы приложение .NET MAUI отправляло и получало уведомления в Android, приложение должно предоставлять реализацию INotificationManagerService интерфейса.

Отправка и получение локальных уведомлений

В Android NotificationManagerService класс реализует INotificationManagerService интерфейс и содержит логику для отправки и получения локальных уведомлений:

C#
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
    }
}

Класс NotificationManagerService должен быть помещен в папку Android платформ > приложения. Кроме того, можно выполнять многонацелие на основе собственных критериев имени файла и папки, а не с помощью папки Android Платформы>. Дополнительные сведения см. в разделе "Настройка многонацеливания".

Android позволяет приложениям определять несколько каналов уведомлений. Конструктор NotificationManagerService создает базовый канал, используемый для отправки уведомлений. Метод SendNotification определяет логику конкретной платформы, необходимую для создания и отправки уведомления. Метод ReceiveNotification вызывается ОС Android при получении сообщения и вызывает NotificationReceived обработчик событий. Дополнительные сведения см. в статье "Создание уведомления по developer.android.com".

Метод SendNotification позволяет создать локальное уведомление сразу или в определенное время (DateTime). Можно запланировать отправку уведомления на определенное время (DateTime) с помощью класса AlarmManager. Это уведомление получит объект, производный от класса BroadcastReceiver.

C#
[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);
        }
    }
}

Важно!

По умолчанию уведомления, запланированные с помощью AlarmManager класса, не будут перезагружать устройство. Однако вы можете создать приложение для автоматического перепланированного уведомления, если устройство перезапущено. Дополнительные сведения см. в разделе Start an alarm when the device restarts (Запуск службы сигнализации при перезагрузке устройства) статьи Schedule repeating alarms (Планирование повторяющихся сигналов) на сайте developer.android.com. Сведения о фоновой обработке в Android см. в разделе Guide to Background processing (Руководство по фоновой обработке) на сайте developer.android.com.

Обработка входящих уведомлений

Приложение Android должно обнаруживать входящие уведомления и уведомлять NotificationManagerService экземпляр. Одним из способов достижения этого является изменение MainActivity класса.

Атрибут Activity в классе MainActivity должен указывать значение LaunchMode для LaunchMode.SingleTop:

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

Режим SingleTop предотвращает запуск нескольких экземпляров Activity , пока приложение находится на переднем плане. Это LaunchMode может быть не подходит для приложений, которые запускают несколько действий в более сложных сценариях уведомлений. Дополнительные сведения о значениях перечисления см. в LaunchMode разделе "Запуск действия Android" на developer.android.com.

Класс MainActivity также необходимо изменить для получения входящих уведомлений:

C#
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);
        }
    }
}

Метод CreateNotificationFromIntent извлекает данные уведомления из аргумента intent и передает его ReceiveNotification методу NotificationManagerService в классе. Метод CreateNotificationFromIntent вызывается как из метода OnCreate, так и с помощью метода OnNewIntent:

  • Когда приложение запускается с помощью данных уведомлений, Intent данные будут переданы методу OnCreate .
  • Если приложение уже находится на переднем плане, Intent данные будут переданы методу OnNewIntent .

Проверка разрешения

Для Android 13 (API 33) и более поздних версий требуется разрешение на отправку POST_NOTIFICATIONS уведомлений из приложения. Это разрешение должно быть объявлено в файле AndroidManifest.xml :

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

Дополнительные сведения о разрешении POST_NOTIFICATIONS см. в разделе "Разрешение среды выполнения уведомлений" на developer.android.com.

Необходимо реализовать класс разрешений, который проверяет объявление разрешений во время выполнения:

C#
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();
        }
    }
}

После запуска приложения необходимо попросить пользователя предоставить это разрешение, прежде чем пытаться отправить локальное уведомление:

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

Дополнительные сведения о разрешениях .NET MAUI см. в разделе "Разрешения".

Реализация локальных уведомлений в iOS и Mac Catalyst

На платформах Apple локальное уведомление — это сообщение, которое передает важную информацию пользователям. Система обрабатывает доставку уведомлений на основе указанного времени или расположения. Сведения о локальных уведомлениях на платформах Apple см. в статье "Планирование уведомления локально из приложения на developer.apple.com".

Чтобы приложение .NET MAUI отправляло и получало уведомления на платформах Apple, приложение должно предоставлять реализацию INotificationManagerService интерфейса.

Отправка и получение локальных уведомлений

На NotificationManagerService платформах Apple класс реализует INotificationManagerService интерфейс и содержит логику для отправки и получения локальных уведомлений:

C#
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
        };
    }
}

Класс NotificationManagerService должен быть помещен в папку "Платформы > iOS " или "Платформы > Mac Catalyst ". Кроме того, можно выполнять многонацелие на основе собственных критериев имени файла и папки, а не с помощью папок Платформ . Дополнительные сведения см. в разделе "Настройка многонацеливания".

На платформах Apple необходимо запросить разрешение на использование уведомлений перед попыткой запланировать уведомление. Это происходит в конструкторе NotificationManagerService . Дополнительные сведения о разрешении локального уведомления см. в статье "Запрос разрешения на использование уведомлений на developer.apple.com".

Метод SendNotification определяет логику, необходимую для создания и отправки уведомления, и создает немедленное локальное уведомление с помощью UNTimeIntervalNotificationTrigger объекта или точного DateTime использования UNCalendarNotificationTrigger объекта. Метод ReceiveNotification вызывается iOS при получении сообщения и вызывает NotificationReceived обработчик событий.

Обработка входящих уведомлений

Для обработки входящих сообщений на платформах Apple необходимо создать делегат, который подклассы UNUserNotificationCenterDelegate:

C#
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);
    }
}

Класс NotificationReceiver регистрируется в качестве UNUserNotificationCenter делегата в конструкторе NotificationManagerService и предоставляет входящие данные уведомления методу ReceiveNotificationNotificationManagerService в классе.

Реализация локальных уведомлений в Windows

Локальные уведомления в пакете SDK для приложений Windows — это сообщения, которые приложение может отправлять пользователю, пока они не находятся в вашем приложении. Содержимое уведомления отображается в временном окне в правом нижнем углу экрана и в Центре уведомлений. Локальные уведомления можно использовать для информирования пользователя о состоянии приложения или запроса пользователя на принятие действия.

Сведения о локальных уведомлениях в Windows, включая сведения о реализации упакованных и распакованных приложений, см. в обзоре уведомлений о приложениях.

Регистрация реализаций платформы

Каждая NotificationManagerService реализация должна быть зарегистрирована в интерфейсе INotificationManagerService , чтобы операции, предоставляемые интерфейсом, можно вызывать из кроссплатформенного кода. Это можно сделать, регистрируя типы со Services свойством MauiAppBuilder объекта в методе CreateMauiAppMauiProgram в классе:

C#
#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

Дополнительные сведения о внедрении зависимостей в .NET MAUI см. в разделе "Внедрение зависимостей".

Отправка и получение локальных уведомлений

Реализация INotificationManagerService может быть разрешена с помощью автоматического разрешения зависимостей или путем явного разрешения зависимостей. В следующем примере показано использование явного INotificationManagerService разрешения зависимостей для разрешения реализации:

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

Дополнительные сведения о разрешении зарегистрированных типов см. в разделе "Разрешение".

INotificationManagerService После разрешения реализации его операции можно вызвать:

C#
// 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.
    });
};
}

Обработчик NotificationReceived событий приводит аргументы события к NotificationEventArgsаргументам, определяющим Title и Message свойствам:

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

В Android, когда уведомление отправляется, оно отображается как значок в строке состояния:

Значок уведомления в строке состояния в Android.

Когда проводите пальцем вниз по строке состояния, откроется панель уведомлений:

Локальное уведомление в ящике уведомлений в Android.

Касание уведомления запускает приложение. Уведомление остается видимым в ящике уведомлений, пока оно не будет отклонено приложением или пользователем.

В iOS входящие уведомления автоматически получаются приложением, не требуя ввода пользователем:

Уведомление в iOS.

В Mac Catalyst входящие уведомления автоматически получаются центром уведомлений:

Уведомление в Центре уведомлений в macOS.

Дополнительные сведения о Центре уведомлений см. в разделе "Центр уведомлений" на компьютере Mac на support.apple.com.