Прослушиватель уведомлений: доступ ко всем уведомлениям

Прослушиватель уведомлений предоставляет доступ к уведомлениям пользователя. Смарт-часы и другие носимые устройства могут использовать прослушиватель уведомлений для отправки уведомлений с телефона на носимое устройство. Приложения домашней автоматизации могут использовать прослушиватель уведомлений для выполнения определенных действий при получении уведомлений, таких как мигание света при получении звонка.

Важно!

Требуется юбилейное обновление. Для использования прослушивателя уведомлений необходимо использовать пакет SDK 14393 и сборку 14393 или более поздней версии.

Важные API-интерфейсы: класс UserNotificationListener, класс UserNotificationChangedTrigger

Включите прослушиватель, добавив функцию пользовательских уведомлений

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

  1. В Visual Studio в проводнике решений дважды щелкните файл Package.appxmanifest, чтобы открыть конструктор манифестов.
  2. Откройте вкладку "Возможности".
  3. Проверьте функцию Прослушиватель пользовательских уведомлений.

Проверьте, поддерживается ли прослушиватель

Если ваше приложение поддерживает более ранние версии Windows 10, необходимо использовать для проверки класс ApiInformation. Если прослушиватель не поддерживается, не отправляйте никакие вызовы в API-интерфейсы прослушивателя.

if (ApiInformation.IsTypePresent("Windows.UI.Notifications.Management.UserNotificationListener"))
{
    // Listener supported!
}
 
else
{
    // Older version of Windows, no Listener
}

Запрос доступа к прослушивателю

Поскольку прослушиватель разрешает доступ к уведомлениям пользователя, пользователи обязаны предоставить приложению разрешение на доступ к уведомлениям. Во время первого запуска приложения необходимо запросить доступ на использование прослушивателя уведомлений. При желании можно продемонстрировать предварительный пользовательский интерфейс, поясняющий, почему приложению требуется доступ к уведомлениям пользователя, прежде чем можно вызвать метод RequestAccessAsync, чтобы пользователь понял, почему они должны разрешить доступ.

// Get the listener
UserNotificationListener listener = UserNotificationListener.Current;
 
// And request access to the user's notifications (must be called from UI thread)
UserNotificationListenerAccessStatus accessStatus = await listener.RequestAccessAsync();
 
switch (accessStatus)
{
    // This means the user has granted access.
    case UserNotificationListenerAccessStatus.Allowed:
 
        // Yay! Proceed as normal
        break;
 
    // This means the user has denied access.
    // Any further calls to RequestAccessAsync will instantly
    // return Denied. The user must go to the Windows settings
    // and manually allow access.
    case UserNotificationListenerAccessStatus.Denied:
 
        // Show UI explaining that listener features will not
        // work until user allows access.
        break;
 
    // This means the user closed the prompt without
    // selecting either allow or deny. Further calls to
    // RequestAccessAsync will show the dialog again.
    case UserNotificationListenerAccessStatus.Unspecified:
 
        // Show UI that allows the user to bring up the prompt again
        break;
}

Пользователь может отозвать этот доступ в любой момент в настройках Windows. Поэтому приложение всегда должно проверка состояние доступа с помощью метода GetAccessStatus перед выполнением кода, использующего прослушиватель уведомлений. Если пользователь отзывает доступ, API-вызовы будут автоматически завершаться сбоем, исключение создаваться не будет (например, API для получения всех уведомлений просто вернет пустой список).

Доступ к уведомлениям пользователя

Прослушиватель уведомлений позволяет получить список текущих уведомлений пользователя. Просто вызовите метод GetNotificationsAsync и задайте тип уведомлений, которые требуется получить (в настоящее время поддерживается только один тип уведомлений — всплывающие уведомления).

// Get the toast notifications
IReadOnlyList<UserNotification> notifs = await listener.GetNotificationsAsync(NotificationKinds.Toast);

Отображение уведомлений

Каждое уведомление представляется в виде класса UserNotification, который предоставляет информацию о приложении, из которого поступает уведомление, времени создания уведомления, идентификаторе уведомления и самом уведомлении.

public sealed class UserNotification
{
    public AppInfo AppInfo { get; }
    public DateTimeOffset CreationTime { get; }
    public uint Id { get; }
    public Notification Notification { get; }
}

Свойство AppInfo предоставляет информацию, необходимую для отображения уведомления.

Примечание

Рекомендуется окружить весь код для обработки одного уведомления в блок try/catch, если непредвиденное исключение произойдет при захвате отдельного уведомления. Не следует отказываться от отображения всех остальных уведомлений из-за проблемы с одним из них.

// Select the first notification
UserNotification notif = notifs[0];
 
// Get the app's display name
string appDisplayName = notif.AppInfo.DisplayInfo.DisplayName;
 
// Get the app's logo
BitmapImage appLogo = new BitmapImage();
RandomAccessStreamReference appLogoStream = notif.AppInfo.DisplayInfo.GetLogo(new Size(16, 16));
await appLogo.SetSourceAsync(await appLogoStream.OpenReadAsync());

Содержимое самого уведомления, например текст уведомления, содержится в свойстве Уведомление. Это свойство содержит визуальную часть уведомления. (Если вы знакомы с отправкой уведомлений в Windows, вы заметите, что свойства Visual и Visual.Bindings в объекте Notification соответствуют тому, что отправляют разработчики при отображении уведомления).

Нужно искать всплывающую привязку (чтобы избежать ошибок в коде, необходимо убедиться, что привязка не равна null). Можно получить текстовые элементы из привязки. Можно отобразить столько текстовых элементов, сколько нужно. (В идеале необходимо отобразить их все.) Текстовые элементы можно обрабатывать иначе: например, первый как текст заголовка, а последующие — как текст основной части.

// Get the toast binding, if present
NotificationBinding toastBinding = notif.Notification.Visual.GetBinding(KnownNotificationBindings.ToastGeneric);
 
if (toastBinding != null)
{
    // And then get the text elements from the toast binding
    IReadOnlyList<AdaptiveNotificationText> textElements = toastBinding.GetTextElements();
 
    // Treat the first text element as the title text
    string titleText = textElements.FirstOrDefault()?.Text;
 
    // We'll treat all subsequent text elements as body text,
    // joining them together via newlines.
    string bodyText = string.Join("\n", textElements.Skip(1).Select(t => t.Text));
}

Удаление конкретного уведомления

Если носимое устройство или служба позволяет пользователю скрыть уведомления, можно удалить фактическое уведомление, чтобы пользователь позже не увидел его на телефоне или на ПК. Просто укажите ИД уведомления (из объекта UserNotification), которое необходимо удалить:

// Remove the notification
listener.RemoveNotification(notifId);

Удалить все уведомления

Метод UserNotificationListener.ClearNotifications удаляет все уведомления пользователя. Используйте этот метод с осторожностью. Удалять все уведомления нужно лишь в том случае, если ваше носимое устройство или служба отображает ВСЕ уведомления. Если носимое устройство или служба отображает только определенные уведомления, то когда пользователь нажимает кнопку "Очистить уведомления", он ожидает, что удалены будут лишь некоторые уведомления; если вызвать метод ClearNotifications, удалятся все уведомления, включая те, которые не отображались на носимом устройстве или в службе.

// Clear all notifications. Use with caution.
listener.ClearNotifications();

Фоновая задача для добавленного/скрытого уведомления

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

Благодаря однопроцессной модели, добавленной в юбилейном обновлении, добавить фоновые задачи относительно просто. В коде приложения main после получения доступа пользователя к прослушивателю уведомлений и доступа к выполнению фоновых задач путем вызова UserNotificationListener.Current.RequestAccessAsync и BackgroundExecutionManager.RequestAccessAsync соответственно, просто зарегистрируйте новую фоновую задачу и задайте UserNotificationChangedTrigger с помощью типа всплывающего уведомления.

// TODO: Request/check Listener access via UserNotificationListener.Current.RequestAccessAsync
 
// TODO: Request/check background task access via BackgroundExecutionManager.RequestAccessAsync
 
// If background task isn't registered yet
if (!BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("UserNotificationChanged")))
{
    // Specify the background task
    var builder = new BackgroundTaskBuilder()
    {
        Name = "UserNotificationChanged"
    };
 
    // Set the trigger for Listener, listening to Toast Notifications
    builder.SetTrigger(new UserNotificationChangedTrigger(NotificationKinds.Toast));
 
    // Register the task
    builder.Register();
}

Затем в файле App.xaml.cs переопределите метод OnBackgroundActivated, если вы еще не сделали этого, и используйте оператор-переключатель в имени задачи, чтобы определить, какой из многочисленных триггеров фоновых задач был вызван.

protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
    var deferral = args.TaskInstance.GetDeferral();
 
    switch (args.TaskInstance.Task.Name)
    {
        case "UserNotificationChanged":
            // Call your own method to process the new/removed notifications
            // The next section of documentation discusses this code
            await MyWearableHelpers.SyncNotifications();
            break;
    }
 
    deferral.Complete();
}

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

SyncNotifications — это метод, который вы реализуете; в следующем разделе показано, как.

Определение добавленных и удаленных уведомлений

В вашем методе SyncNotifications необходимо вычислить разницу между текущей коллекцией уведомлений и уведомлениями на платформе, чтобы определить, какие уведомления были добавлены или удалены (синхронизируя уведомления с носимым устройством).

// Get all the current notifications from the platform
IReadOnlyList<UserNotification> userNotifications = await listener.GetNotificationsAsync(NotificationKinds.Toast);
 
// Obtain the notifications that our wearable currently has displayed
IList<uint> wearableNotificationIds = GetNotificationsOnWearable();
 
// Copy the currently displayed into a list of notification ID's to be removed
var toBeRemoved = new List<uint>(wearableNotificationIds);
 
// For each notification in the platform
foreach (UserNotification userNotification in userNotifications)
{
    // If we've already displayed this notification
    if (wearableNotificationIds.Contains(userNotification.Id))
    {
        // We want to KEEP it displayed, so take it out of the list
        // of notifications to remove.
        toBeRemoved.Remove(userNotification.Id);
    }
 
    // Otherwise it's a new notification
    else
    {
        // Display it on the Wearable
        SendNotificationToWearable(userNotification);
    }
}
 
// Now our toBeRemoved list only contains notification ID's that no longer exist in the platform.
// So we will remove all those notifications from the wearable.
foreach (uint id in toBeRemoved)
{
    RemoveNotificationFromWearable(id);
}

Событие переднего плана для добавленных/удаленных уведомлений

Важно!

Известная проблема. В сборках, предшествующих сборке 17763, обновлению за октябрь 2018 г. или версии 1809, событие переднего плана вызывает цикл ЦП и (или) не работает. Если вам нужна поддержка в предыдущих сборках, используйте фоновую задачу.

Вы также можете прослушивать уведомления от обработчика событий в памяти...

// Subscribe to foreground event
listener.NotificationChanged += Listener_NotificationChanged;
 
private void Listener_NotificationChanged(UserNotificationListener sender, UserNotificationChangedEventArgs args)
{
    // Your code for handling the notification
}

Как устранить задержки в фоновой задаче

При тестировании приложения вы можете заметить, что фоновая задача иногда откладывается и не запускается в течение нескольких минут. Чтобы устранить задержку, предложите пользователю перейти к системным параметрам —> Система —> Батарея —> Использование батареи по приложению, найдите свое приложение в списке, выберите его и задайте для него значение "Всегда разрешено в фоновом режиме". После этого фоновая задача всегда должна запускаться примерно через секунду после получения уведомления.