Среди сотен новых средств в Windows 8.1 появилась новая концепция приложения для оповещений (alarm app). В двух словах, это приложение Windows Store, которое позволяет планировать всплывающие уведомления (toast notifications) с точностью до секунды. Из-за того, как Windows обрабатывает такие уведомления, точность их выдачи недостаточна для большинства приложений. В этой статье я исследую концепцию приложения для оповещений и рассмотрю, что нужно для разработки собственного такого приложения.
Что такое приложение для оповещений?
Прежде чем заглянуть «под капот» подобного приложения, важно понять, какой тип программ позволил бы создавать хорошие приложения для оповещений. Ключ к пониманию этого — в точности доставки уведомлений. Например, приложению-календарю не обязательно уведомлять пользователя о дне рождения его друга с точностью до секунды. Однако есть несколько типов приложений, которым эта точность действительно требуется.
Самый очевидный выбор — доброе старомодное приложение-будильник. Когда пользователь устанавливает сигнал на какое-то время, приложение должно уведомить его именно в этот момент. Теперь вы можете создавать более точные приложения для оповещения в строго определенного время, такие как Pomodoro. Приложение для оповещений можно было бы также использовать для программ интервальных тренировок по системе Tabata, где длительность упражнений и перерывов между ними очень короткая и эти интервалы крайне важны.
Все разработчики приложений Windows Store должны быть знакомы с концепцией приложений экрана блокировки (lock screen) в Windows 8. Приложения, помещаемые на экран блокировки, могут обновлять этот экран. Кроме того, приложение экрана блокировки получает доступ к более широкому кругу возможностей, чем обычное приложение. Это важно, когда вашему приложению нужно осуществлять такие операции, как обновление экрана блокировки, или выполнять множество фоновых задач.
Потребность в дополнительных возможностях приложений экрана блокировки — прямой результат изменений в Windows 8, внесенных для повышения производительности и продления срока работы аккумуляторов. Windows предотвращает выполнение приложений, когда они не используются, и по возможности консолидирует задачи. По этой причине вы обнаружите, что планируемые события, такие как фоновые задачи и уведомления, будут всегда запускаться не точно в указанное время, а приблизительно. Windows объединяет в пакеты обновления и выполняет их, когда процесс готов к работе.
В Windows 8 вы могли определить до семи приложений, размещаемых на экране блокировки в любой момент. В эти семь слотов входил и специальный слот для приложения, сообщающего подробное состояние. Среди этих семи приложений лишь одно могло предоставлять свой UI на экране блокировки. В Windows 8.1 эта структура в целом сохранена, но приложение для оповещений добавлено как особый новый тип приложений экрана блокировки. На рис. 1 показана новая часть настроек «Lock screen apps». Единовременно на устройстве может выполняться лишь одно приложение для оповещений — так же, как приложение, которое сообщает подробное состояние.
Рис. 1. Часть настроек экрана блокировки на ПК
Единственная разница между приложением для оповещений и другими приложениями экрана блокировки состоит в возможности вывода всплывающих уведомлений в пределах одной секунды запланированного времени срабатывания. Как уже упоминалось, передача или планирование всплывающего уведомления в приложении Windows Store не гарантирует, что это уведомление будет доставлено в точное время. Оно доставляется в течение временного окна, и конкретное время выбирается самой Windows. Если какое-то приложение идентифицируется пользователем как приложение для оповещений, то запланированные всплывающие уведомления доставляют точно по расписанию.
Настройка приложения для оповещений
Прежде чем какое-либо приложение можно будет выбрать в качестве приложения для оповещений, его манифест нужно должным образом настроить. Если в манифест не включены требуемые средства, попытка программного задания приложения в качестве приложения для оповещений приведет к генерации ошибки, и у пользователя не будет возможности сделать это вручную.
Так как в приложении для оповещений планируются всплывающие уведомления, сначала нужно разрешить такие уведомления. Это делается в дизайнере манифеста на вкладке Application UI. Раскройте узел Visual Assets. Раздел Notifications находится под All Image Assets, как и подгруппа Badge Logo. Чтобы разрешить всплывающие уведомления для приложения, нужно выбрать в поле Toast capable значение Yes.
В разделе Notifications также можно разрешить уведомления на экране блокировки (Lock screen notifications). Как уже упоминалось, приложение для оповещений является особым типом приложений экрана блокировки; поэтому его нужно сконфигурировать на работу с экраном блокировки. Поэтому вы должны указать в поле Lock screen notifications либо Badge, либо Badge and Tile Text.
После этого в манифест нужно добавить несколько дополнительных элементов. Например, приложению должна быть присвоена эмблема — индикатор событий (badge logo). У эмблемы должно быть изображение размером 24×24 пикселя, и, как в случае всех эмблем в приложении Windows Store, вы можете задавать различные размеры для масштабирования, соответствующие разрешению Windows. На рис. 2 показан дизайнер манифеста с включенными всплывающими уведомлениями и уведомлениями на экране блокировки.
Рис. 2. Раздел Notifications в дизайнере манифеста
Любое приложение, для которого разрешены уведомления на экране блокировки, также должно объявлять фоновую задачу в своем манифесте. Интересная часть этого требования в том, что на самом деле вы не обязаны реализовать фоновую задачу в приложении для экрана блокировки; ее надо просто объявить.
Фоновая задача объявляется в разделе Declarations дизайнера манифеста. В Available Declarations выберите Background Tasks и щелкните Add. Фоновая задача для приложения экрана блокировки должна поддерживать один из следующих типов задач: Control channel, Timer, Push notification или Location. В разделе App settings фоновой задачи надо задать значение либо для Entry point, либо для Start page. В XAML-приложении, использующем фоновую задачу, для Entry point указывается класс, реализующий интерфейс IBackgroundTask. Если приложение на самом деле не реализует фоновую задачу, это значение может быть совершенно произвольным. На рис. 3 показана должным образом сконфигурированная фоновая задача, что позволяет добавить приложение на экран блокировки.
Рис. 3. Конфигурирование фоновой задачи
Последний шаг в конфигурировании манифеста — идентификация приложения в качестве оповещающего. Для этого в манифест надо добавить расширение для оповещений. К сожалению, через дизайнер манифеста это расширение добавить нельзя; это придется делать вручную. С этой целью вы должны сначала получить нижележащий XML манифеста пакета: щелкните правой кнопкой мыши package.appxmanifest в Solution Explorer и выберите View Code.
XML в манифесте приложения Windows 8.1 будет использовать два пространства имен. Первое пространство имен (это пространство по умолчанию) содержит элементы, определенные в Windows 8. Второе — добавляется и идентифицируется по префиксу m2. Оно включает дополнения и изменения, появившиеся в Windows 8.1. Так как функциональность приложения для оповещений — новинка Windows 8.1, вы должны добавить расширение «8.1» в набор Extensions в элементе Application. Свойству Category нового расширения должно быть присвоено значение windows.alarm. Вот пример того, как должен выглядеть набор Extensions с новым расширением и ранее объявленной фоновой задачей:
<Extensions>
<Extension Category="windows.backgroundTasks"
EntryPoint="App">
<BackgroundTasks>
<Task Type="timer" />
</BackgroundTasks>
</Extension>
<m2:Extension Category="windows.alarm" />
</Extensions>
Запрос доступа к оповещениям
После внесения необходимых настроек и объявлений ваше приложение может запросить доступ как приложение для оповещений на устройстве. Пользователь может сделать это вручную из PC Settings; однако ваше приложение может запросить доступ через Windows Runtime (WinRT).
WinRT включает статический класс AlarmApplicationManager в пространстве имен Windows.ApplicationModel.Background. Этот класс имеет метод RequestAccessAsync, который запросит у пользователя разрешение на установку приложения как оповещающего (рис. 4). Если другое приложение уже установлено как оповещающее, оно будет заменено текущим, но только в том случае, если пользователь при соответствующем запросе выберет Yes.
Рис. 4. Запрос разрешения для оповещающего приложения
Метод RequestAccessAsync возвращает перечисление AlarmAccessStatus, в котором три допустимых значения: Denied, AllowedWithWakeupCapability и AllowedWithoutWakeupCapability. Это важно, если вы создаете оповещающие приложения, которые, например, выступают по утрам в роли будильников. Если устройство находится в спящем режиме и не настроено на пробуждение посредством всплывающих уведомлений, то ваши будильники не сработают.
Важно помнить, что Windows 8 передает управление устройством пользователю. Как видно на рис. 1, пользователь может легко сменить приложение для оповещений. Ваша проблема в том, что каждое приложение может запросить доступ из AlarmApplicationManager только раз. Если приложение выдает второй запрос на доступ (для данного пользователя и устройства), пользователь не увидит его, и приложение получит лишь текущее состояние оповещений. После того как ваше приложение получило отказ в доступе, было заменено другим приложением или вручную удалено пользователем, у вас остается единственный вариант уведомить пользователя о том, что приложение нужно вручную снова добавить как оповещающее на этом устройстве.
AlarmApplicationManager также включает метод GetAccessStatus, который возвращает текущее состояние оповещений компьютера. Как и в случае метода RequestAccessAsync, он возвращает перечисление AlarmAccessStatus, и это отличный способ определить, что происходит с устройством. Например, ваше приложение могло бы информировать пользователя о том, что уведомления не будут пробуждать устройство.
Планирование оповещений
Оповещения — это просто запланированные всплывающие уведомления с чуть большими возможностями. Процесс планирования оповещения идентичен таковому для всплывающего уведомления; единственное отличие в XML уведомления. XML, используемый для определения уведомления, будет содержать информацию о его внешнем виде, а также любые включенные средства.
Всплывающее уведомление может быть либо текстом, либо комбинацией текста и изображения, что определяется одним из шаблонов, предоставляемых Windows Runtime. В настоящее время вы можете выбирать из восьми разных XML-шаблонов. На момент написания этой статьи Windows 8.1 не поддерживала пользовательские разметки всплывающих уведомлений, поэтому вы должны выбрать один из предоставляемых шаблонов.
Тип и разметка всплывающего уведомления определяется блоком XML. Этот XML идентифицирует используемый шаблон, текст и изображение, заполняющие шаблон, и несколько других параметров, о которых мы поговорим позже. Вот пример базового всплывающего уведомления:
<toast duration="long">
<visual>
<binding template="ToastText02">
<text id="1">Sample Toast App</text>
<text id="2">The is a sample message.</text>
</binding>
</visual>
</toast>
Сгенерировать шаблон всплывающего уведомления можно несколькими способами. Windows Runtime создаст XmlDocument каждого шаблона из ToastNotificationManager, который находится в пространстве имен Windows.UI.Notification. В ToastNotificationManager есть статический метод GetTemplateContent, который принимает перечисление ToastTemplateType со значением для каждого из доступных шаблонов. Вот как получить XmlDocument из предыдущего примера:
XmlDocument content =
ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
content.DocumentElement.SetAttribute("duration", "long");
var textLines = content.GetElementsByTagName("text");
textLines[0].InnerText = "Sample Toast App";
textLines[1].InnerText = "The is a sample message.";
Это хорошая отправная точка; однако в зависимости от области применения такой вариант может оказаться громоздким. Вот почему любые дополнения или модификации для шаблона обычно выполняются через XmlDocument API. Благодаря этому можно обойтись несколькими строками кода для нескольких простых дополнений. С учетом этого нет ничего необычного в том, что XML конструируется в виде строки, а затем загружается в XmlDocument, как только шаблон завершен. На рис. 5 показано, как добиться эквивалента предыдущего примера, генерируя XML в виде строки.
Рис. 5. Генерация XML как строки
string textLine1 = "Sample Toast App";
string textLine2 = "This is a sample message.";
string contentString =
"<toast duration=\"long\">\n" +
"<visual>\n" +
"<binding template=\"ToastText02\">\n" +
"<text id=\"1\">" + textLine1 + "</text>\n" +
"<text id=\"2\">" + textLine2 + "</text>\n" +
"</binding>\n" +
"</visual>\n" +
"</toast>\n";
XmlDocument content = new Windows.Data.Xml.Dom.XmlDocument();
content.LoadXml(contentString);
Если вы пойдете по этому пути, то должны остерегаться двух вещей. Во-первых, вы должны удостовериться, что создаете правильно отформатированный XML — примите меры для создания escape-последовательностей для любых специальных символов, которые могут быть включены в строку. Во-вторых, убедитесь, что вы следуете схеме всплывающего уведомления. Полные спецификации этой схемы вы найдете на сайте Windows Dev Center по ссылке bit.ly/172oYCO.
Во всех предыдущих примерах был один элемент, который не является частью шаблона по умолчанию. Вероятно, вы заметили, что атрибут duration в элементе toast имеет слишком большое значение. Поскольку вы создаете приложение для оповещений, вы вряд ли захотите, чтобы оповещение появлялось, а затем исчезало без участия пользователя. Указав в duration большое значение, вы сможете отображать всплывающее уведомление на экране вплоть до 25 секунд.
После конфигурирования XML и загрузки в XmlDocument можно планировать всплывающие уведомления. Первым делом создайте экземпляр ScheduledToastNotification. Для этого нужны две вещи: XmlDocument, содержащий определение всплывающего уведомления, и время, на которое планируется появление этого уведомления.
ScheduledToastNotification планируется с помощью ToastNotifier. Экземпляр ToastNotifier создается из класса ToastNotificationManager, используя статический метод CreateToastNotifier. После этого вызов метода AddToSchedule из ToastNotifier запланирует ваше уведомление. Всплывающие уведомления обычно создаются в результате каких-то действий пользователя, таких как нажатие кнопки, или события, сгенерированного в приложении. Вот пример создания и планирования всплывающего уведомления на появление спустя минуту после текущего момента:
ToastNotifier toastNotifier =
ToastNotificationManager.CreateToastNotifier();
var scheduledToast = new ScheduledToastNotification(
content, DateTime.Now.AddMinutes(1));
toastNotifier.AddToSchedule(scheduledToast);
В итоге генерируется всплывающее уведомление, как на рис. 6. Заметьте, что оно должно выглядеть подобно большинству других уведомлений в Windows.
Рис. 6. Базовое всплывающее уведомление
Создание уведомления может закончиться неудачей по нескольким причинам. Самая распространенная — XML отформатирован неправильно. Это может быть вызвано неподходящим идентификатором шаблона или тем, что определены не все обязательные атрибуты, поэтому очень важно проверять, что вы создаете корректный XML.
Если вы попытаетесь запланировать уведомление на уже прошедшее время, при вызове AddToSchedule будет сгенерировано исключение. Если вы планируете уведомление на несколько секунд в будущем, убедитесь, что вычисление времени — это одна из последних ваших операций. Если вы задаете время на две секунды от текущего момента, а код выполняется четыре секунды до того, как происходит реальное планирование уведомления, вы получите исключение.
Наконец, ваше приложение может планировать не более 4096 всплывающих уведомлений одновременно. Я знаю, что вы сейчас возразите, что такое немыслимо ни при каких обстоятельствах, но в приложении для оповещений это гораздо проще сделать, чем вы думаете. Допустим, у вас есть оповещающее приложение, которое позволяет планировать ежедневное оповещение. Если ваш код планирует несколько ежедневных оповещений на следующий год, вы достигнете максимума всего при 12 ежедневных оповещениях. Поэтому проверяйте продуманность своих алгоритмов планирования оповещений и убедитесь в том, что вы управляете оповещениями после их создания.
Управление оповещениями и их удаление
Для успешного создания оповещающего приложения у вас должна быть возможность просматривать и удалять любые запланированные оповещения. Обе эти задачи позволяет выполнять ToastNotifier. Вызов метода GetScheduledToastNotifications возвращает список ScheduledToastNotifications только для чтения. Этот набор будет содержать запланированные уведомления для текущего приложения.
Чтобы удалить запланированное уведомление, его надо передать методу RemoveFromSchedule. Как только уведомление выведено на экран и закрыто пользователем, оно больше не появится в наборе уведомлений приложения и не будет учитываться счетчиком запланированных уведомлений. Вот как очистить все запланированные уведомления:
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
var notifications = toastNotifier.GetScheduledToastNotifications();
// Удаляем каждое уведомление из расписания
foreach (var notification in notifications)
{
toastNotifier.RemoveFromSchedule(notification);
}
Вы могли бы, например, поместить этот код в обработчик команды или события, связанный с кнопкой Clear All.
Добавление команд
Если ваше приложение в данный момент является оповещающим для устройства, тогда предыдущий пример кода, генерировавшего всплывающее уведомление (рис. 6), выведет его вовремя, но уведомление на рис. 6 не сильно похоже на оповещение, не правда ли? В основном здесь не хватает пары распространенных для оповещений средств: повтора (snooze) и удаления. По умолчанию щелчок всплывающего уведомления или жест смахивания приведет к его удалению; однако оповещение должно быть чуточку более интуитивно понятным.
Элемент <toast> поддерживает не только дочерний элемент <visual>, но и <commands>. Этот элемент позволяет добавлять к уведомлению предопределенные команды. Сам элемент поддерживает единственный необязательный атрибут scenario. Данный атрибут можно присвоить либо оповещению, либо incomingCall. Здесь для моих целей он всегда будет присваиваться оповещению.
Элемент <commands> содержит набор индивидуальных элементов <command>. У каждой команды должен быть атрибут id, указывающий тип данной команды. Для оповещения значение этого атрибута может быть либо snooze, либо dismiss. Добавив раздел commands к предыдущему всплывающему уведомлению, вы сделаете его несколько более похожим на оповещение: его можно будет не только удалять, но и повторять. Вот только что сгенерированный код всплывающего уведомления (результат работы этого кода см. на рис. 7):
<toast duration="long">
<visual>
<binding template="ToastText02">
<text id="1">Sample Toast App</text>
<text id="2">The is a sample message.</text>
</binding>
</visual>
<commands scenario="alarm">
<command id="snooze"/>
<command id="dismiss"/>
</commands>
</toast>
Рис. 7. Всплывающее уведомление — оповещение
Повтор оповещения
По умолчанию Windows задает время повтора для оповещения равным девяти минутам. Но вы можете определить свое время повтора в классе ScheduledToastNotification. В этом классе есть второй конструктор с двумя дополнительными параметрами. Первый — это интервал повтора. Он определяется как TimeSpan и может варьироваться от 1 до 60 минут.
Второй новый параметр — максимальное число повторений. Если обнулить его значение, оповещение будет повторяться бесконечно. Вот пример задания частоты повтора:
DateTime scheduledTime = DateTime.Now.AddMinutes(1);
TimeSpan snoozeInterval = TimeSpan.FromMinutes(5);
var scheduledToast = new ScheduledToastNotification(
content, scheduledTime, snoozeInterval, 0);
Изменение звука оповещения
Если пользователь включил звук для уведомлений, тогда каждое оповещение будет сопровождаться тем же звуком «динь», что и при выводе всплывающих уведомлений. Это вызывает несколько проблем, когда дело доходит до создания оповещающего приложения. Во-первых, пользователь привыкает слышать один и тот же звук для всех уведомлений. То есть звук оповещения может утонуть в шуме других уведомлений, и оповещение может пройти незамеченным. Во-вторых, оповещение обычно издает звук, пока пользователь не примет его. Значит, звук для оповещения нужно сделать циклическим.
К счастью, определение всплывающего уведомления также поддерживает элемент <audio>, который позволяет настраивать звук, издаваемый при появлении уведомления. Элемент <audio> имеет два атрибута: src и loop. Прежде чем вы слишком разгорячитесь, замечу, что значение атрибута src должно быть одним из предопределенных в Windows. То есть вы не можете предоставить свой звуковой сигнал, но располагаете вполне приличным набором звуков, из которых есть что выбрать.
У src должно быть одно из 25 предопределенных значений. Звуки разбиваются на две категории: с повтором и без. Повторяющиеся звуки конструируются так, чтобы обеспечить непрерывное звучание, и их следует использовать при создании уведомления с циклическим звуком. Вам предоставляется по 10 зацикленных звуков для оповещений и входящих звонков. Полный список предопределенных значений см. по ссылке bit.ly/16HV2xm.
Если вы хотите воспроизводить циклический звук, установите атрибут loop в true. Кроме того, атрибут duration в элементе <toast> должен быть установлен в long, чтобы дать время для проигрывания звука. На рис. 8 показан пример задания циклического звука для всплывающего уведомления.
Рис. 8. Задание циклического звука для всплывающего уведомления
<toast duration="long">
<visual>
<binding template="ToastText02">
<text id="1">Sample Toast App</text>
<text id="2">The is a sample message.</text>
</binding>
</visual>
<commands scenario="alarm">
<command id="snooze"/>
<command id="dismiss"/>
</commands>
<audio src="ms-winsoundevent:Notification.Looping.Alarm2"
loop="true" />
</toast>
Если при выводе уведомления никаких звуков не требуется, можно установить атрибут silent в true. Тем самым вы замените все настройки по умолчанию для вывода всплывающих уведомлений. Как это делается, показано на рис. 9.
Рис. 9. Задание атрибута silent, чтобы уведомление отображалось без звука
<toast duration="long">
<visual>
<binding template="ToastText02">
<text id="1">Sample Toast App</text>
<text id="2">The is a sample message.</text>
</binding>
</visual>
<commands scenario="alarm">
<command id="snooze"/>
<command id="dismiss"/>
</commands>
<audio silent="true" />
</toast>
Заключение
Приложение для оповещений можно развивать во многих других направлениях. Поскольку на каждом устройстве может быть только одно приложение для оповещений, важно тщательно продумать его. Если область применения вашего приложения слишком ограничена, пользователь может заменить его другим приложением. Это, конечно, зависит от задач вашего приложения и его целевой аудитории. Подумайте о таких вещах, как использование контракта Share Target, чтобы другие приложения могли планировать оповещения через ваше приложение.
Благодаря добавлению функциональности оповещающего приложения в Windows 8.1 теперь можно создавать широкий спектр приложений, основанных на таймерах высокой точности. Я показал, что нужно для создания и запуска оповещающего приложения.