Статьи

Windows Phone 8: кратко: интеграция с операционной системой

В этом руководстве мы собираемся изучить различные способы интеграции нашего приложения с функциями, предлагаемыми платформой Windows Phone. Мы рассмотрим варианты запуска и выбора, узнаем, как взаимодействовать с контактами и встречами, а также узнаем, как воспользоваться преимуществами Kid’s Corner — инновационной функции, которая позволяет детям безопасно пользоваться телефоном.

Когда мы обсуждали хранилище ранее в этой серии, мы ввели понятие изолированных приложений. Точно так же, как хранилище изолировано так, что вы не можете получить доступ к данным, хранящимся в другом приложении, само приложение изолировано от операционной системы.

Самым большим преимуществом этого подхода является безопасность. Даже если вредоносное приложение сможет пройти процесс сертификации, оно не сможет нанести значительный ущерб, поскольку не имеет прямого доступа к операционной системе. Но рано или поздно вам придется взаимодействовать с одной из многих функций Windows Phone, таких как отправка сообщения, телефонный звонок, воспроизведение песни и т. Д.

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

Пусковые установки — это API «запускай и забывай». Вы требуете операции и ничего не ожидаете взамен — например, телефонный звонок или воспроизведение видео.

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

Все средства запуска и выбора доступны в пространстве имен Microsoft.Phone.Tasks и имеют одинаковое поведение:

  • Каждый модуль запуска и выбора представлен конкретным классом.
  • При необходимости вы устанавливаете некоторые свойства, которые используются для определения настроек программы запуска или выбора.
  • С помощью выбора вы должны подписаться на событие Completed , которое срабатывает после завершения операции.
  • Метод Show() вызывается для выполнения задачи.

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

В следующем примере вы можете увидеть средство запуска, которое отправляет электронное письмо с EmailComposeTask класса EmailComposeTask :

01
02
03
04
05
06
07
08
09
10
11
private void OnComposeMailClicked(object sender, RoutedEventArgs e)
{
    EmailComposeTask mailTask = new EmailComposeTask();
 
    mailTask.To = “[email protected]”;
    mailTask.Cc = “[email protected]”;
    mailTask.Subject = “Subject”;
    mailTask.Body = “Body”;
 
    mailTask.Show();
}

В следующем примере показано, как использовать селектор. Мы собираемся сохранить новый контакт в People Hub с SaveContactTask класса SaveContactTask .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private void OnSaveContactClicked(object sender, RoutedEventArgs e)
{
    SaveContactTask task = new SaveContactTask();
    task.Completed += task_Completed;
 
    task.FirstName = “John”;
    task.LastName = “Doe”;
    task.MobilePhone = “1234567890”;
             
    task.Show();
}
 
void task_Completed(object sender, SaveContactResult e)
{
    if (e.TaskResult == TaskResult.OK)
    {
        MessageBox.Show(“The contact has been saved successfully”);
    }
}

Каждый chooser возвращает свойство TaskResult со статусом операции. Важно убедиться, что статус TaskResult.OK прежде чем двигаться дальше, потому что пользователь мог отменить операцию.

Ниже приведен список всех доступных пусковых установок:

  • MapsDirectionTask используется для открытия собственного приложения Map и вычисления пути между двумя местами.
  • MapsTask используется для открытия собственного приложения Map с центром в определенном месте.
  • MapDownloaderTask используется для управления поддержкой автономных карт, новой для Windows Phone 8. С помощью этой задачи вы сможете открыть страницу настроек, используемую для управления загруженными картами.
  • MapUpdaterTask используется для перенаправления пользователя на определенную страницу настроек для проверки наличия обновлений автономных карт.
  • ConnectionSettingsTask используется для быстрого доступа к различным страницам настроек для управления различными доступными соединениями, такими как Wi-Fi, сотовая связь или Bluetooth.
  • EmailComposeTask используется для подготовки электронного письма и его отправки.
  • MarketplaceDetailTask используется для отображения страницы сведений о приложении в Магазине Windows Phone. Если вы не предоставите идентификатор приложения, откроется страница сведений о текущем приложении.
  • MarketplaceHubTask используется для открытия магазина определенной категории.
  • MarketplaceReviewTask используется для открытия страницы в Магазине Windows Phone, где пользователь может оставить отзыв о текущем приложении.
  • MarketplaceSearchTask используется для запуска поиска по определенному ключевому слову в Магазине.
  • MediaPlayerLauncher используется для воспроизведения аудио или видео с помощью внутреннего проигрывателя Windows Phone. Он может воспроизводить как файлы, встроенные в проект Visual Studio, так и файлы, сохраненные в локальном хранилище.
  • PhoneCallTask используется для начала телефонного звонка.
  • ShareLinkTask используется для ShareLinkTask ссылки в социальной сети с помощью встроенных социальных функций Windows Phone.
  • ShareStatusTask используется для обмена пользовательским текстом статуса в социальной сети.
  • ShareMediaTask используется, чтобы поделиться одной из картинок из Photos Hub в социальной сети.
  • SmsComposeTask используется для подготовки текстового сообщения и его отправки.
  • WebBrowserTask используется для открытия URI в Internet Explorer для Windows Phone.
  • SaveAppointmentTask используется для сохранения встречи в собственном приложении Календарь.

Ниже приведен список доступных селекторов:

  • AddressChooserTask используется для импорта адреса контакта.
  • CameraCaptureTask используется для съемки с помощью встроенной камеры и импорта ее в приложение.
  • EmailAddressChooserTask используется для импорта адреса электронной почты контакта.
  • PhoneNumberChooserTask используется для импорта номера телефона контакта.
  • PhotoChooserTask используется для импорта фотографии из центра фотографий.
  • SaveContactTask используется для сохранения нового контакта в People Hub. Селектор просто возвращает, была ли операция успешно завершена.
  • SaveEmailAddressTask используется для добавления нового адреса электронной почты к существующему или новому контакту. Селектор просто возвращает, была ли операция успешно завершена.
  • SavePhoneNumberTask используется для добавления нового номера телефона к существующему контакту. Селектор просто возвращает, была ли операция успешно завершена.
  • SaveRingtoneTask используется для сохранения новой мелодии звонка (которая может быть частью проекта или храниться в локальном хранилище). Возвращает, была ли операция успешно завершена.

Средства запуска уже предоставляют базовый способ взаимодействия с People Hub, но они всегда требуют взаимодействия с пользователем. Они открывают People Hub, и пользователь должен выбрать, какой контакт импортировать.

Однако в определенных сценариях вам нужна возможность программно получать контакты и встречи для данных. Windows Phone 7.5 представила несколько новых API для удовлетворения этого требования. Вам просто нужно помнить, что для соблюдения ограничений безопасности Windows Phone эти API работают только в режиме только для чтения; вы сможете получать данные, но не сохраняете их (далее в этой статье мы увидим, что в Windows Phone 8 появился способ переопределить это ограничение).

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

поставщик

Контактное лицо

Изображение контакта

Дополнительная информация

Календарь назначений

устройство

да

да

да

да

Outlook.com

да

да

да

да

обмен

да

да

да

да

SIM

да

да

да

нет

facebook

да

да

да

нет

Другие социальные сети

нет

нет

нет

нет

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

Каждый контакт представлен классом Contact , который содержит всю информацию о контакте, такую ​​как DisplayName , Addresses , EmailAddresses , EmailAddresses Birthdays и т. Д. (В основном, вся информация, которую вы можете редактировать при создании нового контакта в People Hub) ,

Примечание. Для доступа к контактам необходимо включить параметр ID_CAP_CONTACTS в файле манифеста.

Взаимодействие с контактами начинается с класса « Contacts », который можно использовать для поиска с помощью SearchAsync() . Метод требует два параметра: ключевое слово и фильтр для применения. Есть два способа начать поиск:

  • Общий поиск: ключевое слово не требуется, так как вы просто получите все контакты, которые соответствуют выбранному фильтру. Этот тип поиска может быть реализован с помощью двух типов фильтров: FilterKind.PinnedToStart который возвращает только контакты, которые пользователь FilterKind.None на начальном экране, и FilterKind.None который просто возвращает все доступные контакты.
  • Поиск определенного поля. В этом случае ключевое слово поиска будет применяться на основе выбранного фильтра. Доступные фильтры: DisplayName , EmailAddress и PhoneNumber .

Метод SearchAsync() использует метод обратного вызова; По завершении поиска вызывается событие SearchCompleted .

В следующем примере вы можете найти поиск, который ищет все контакты, которых зовут Джон. Коллекция возвращенных контактов представляется пользователю с помощью ListBox управления ListBox .

01
02
03
04
05
06
07
08
09
10
11
private void OnStartSearchClicked(object sender, RoutedEventArgs e)
{
    Contacts contacts = new Contacts();
    contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
    contacts.SearchAsync(«John», FilterKind.DisplayName, null);
}
 
void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
    Contacts.ItemsSource = e.Results;
}

Совет: если вы хотите начать поиск другого поля, которое не включено в доступные фильтры, вам нужно получить список всех доступных контактов с помощью параметра FilterKind.None и применить фильтр с помощью запроса LINQ. Разница в том, что встроенные фильтры оптимизированы для повышения производительности, поэтому используйте подход LINQ только в том случае, если вам нужно найти поле, отличное от имени, адреса электронной почты или номера телефона.

Получение данных из календаря работает очень схожим образом: каждое назначение идентифицируется классом « Appointment », который имеет такие свойства, как « Subject , « Status , « Location , « EndTime и « EndTime .

Чтобы взаимодействовать с календарем, вам нужно использовать класс Appointments который, как и класс Contacts , использует метод SearchAsync() для запуска поиска и событие SearchCompleted для возврата результатов.

Для выполнения поиска требуются только два параметра: дата начала и дата окончания. Вы получите взамен все встречи в течение этого времени. При желании вы также можете установить максимальное количество результатов для возврата или ограничить поиск конкретной учетной записью.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
private void OnStartSearchClicked(object sender, RoutedEventArgs e)
{
    Appointments calendar = new Appointments();
    calendar.SearchCompleted += calendar_SearchCompleted;
    DateTime start = DateTime.Now.AddMonths(-1);
    DateTime end = DateTime.Now;
    calendar.SearchAsync(start, end, null);
}
 
void calendar_SearchCompleted(object sender, AppointmentsSearchEventArgs e)
{
    Calendar.ItemsSource = e.Results;
}

Совет: Единственный способ отфильтровать результаты по дате начала и дате окончания. Если вам необходимо применить дополнительные фильтры, вам нужно будет выполнить LINQ-запросы к результатам, возвращаемым операцией поиска.

Самое большое ограничение API контактов, которое мы видели до сих пор, это то, что мы можем только читать данные, а не записывать их. В некоторых ситуациях требуется добавить контакты в People Hub без запроса разрешения пользователя, например приложение социальной сети, которое хочет добавить своих друзей в список контактов, или клиент синхронизации, который необходимо сохранить. информация из стороннего облачного сервиса в вашей контактной книге.

В Windows Phone 8 появился новый класс ContactStore , представляющий личную книгу контактов для приложения. С точки зрения пользователя, он ведет себя как обычный источник контактов (например, Outlook.com, Facebook или Gmail). Пользователь сможет видеть контакты в People Hub, смешанные со всеми другими обычными контактами.

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

Функция ContactStore, отображаемая в доступных аккаунтах People Hubs

Класс ContactStore принадлежит пространству имен Windows.Phone.PersonalInformation и предлагает метод с именем CreateOrOpenAsync() . Метод должен вызываться каждый раз, когда вам нужно взаимодействовать с личной книгой контактов. Если он не существует, он будет создан; в противном случае он будет просто открыт.

При создании ContactStore вы можете указать, как операционная система должна предоставлять к нему доступ:

  • Тип первого параметра — ContactStoreSystemAccessMode , и он используется для выбора того, сможет ли приложение редактировать только контакты, принадлежащие частному хранилищу ( ReadOnly ), или пользователь также сможет редактировать информацию с помощью People Hub ( ReadWrite ).
  • Тип второго параметра — ContactStoreApplicationAccessMode , и он используется для выбора того, смогут ли другие сторонние приложения получить доступ ко всей информации о наших контактах ( ReadOnly ) или только к наиболее важным, таким как имя и изображение ( LimitedReadOnly ).

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

1
2
3
4
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
}

Совет: После создания частного хранилища вы не можете изменить определенные вами разрешения, поэтому вам всегда нужно вызывать метод CreateOrOpenAsync() с теми же параметрами.

Контакт определяется классом StoredContact , который немного отличается от класса Contact мы видели ранее. В этом случае единственными доступными свойствами являются GivenName и FamilyName . Доступ ко всем остальным свойствам можно получить, вызвав метод GetPropertiesAsync() класса StoredContact , который возвращает коллекцию типа Dictionary<string, object> .

Каждый элемент коллекции идентифицируется ключом (имя свойства контакта) и объектом (значение). Чтобы помочь разработчикам получить доступ к свойствам, все доступные ключи хранятся в объекте enum с именем KnownContactProperties . В следующем примере мы используем ключ KnowContactProperties.Email для хранения адреса электронной почты пользователя.

01
02
03
04
05
06
07
08
09
10
11
12
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
 
    StoredContact contact = new StoredContact(store);
    contact.GivenName = «Matteo»;
    contact.FamilyName = «Pagani»;
    IDictionary<string, object> properties = await contact.GetPropertiesAsync();
    properties.Add(KnownContactProperties.Email, «[email protected]»);
 
    await contact.SaveAsync();
}

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

Класс StoredContact также поддерживает способ хранения пользовательской информации путем доступа к расширенным свойствам с помощью GetExtendedPropertiesAsync() . Он работает как стандартные свойства, за исключением того, что ключ свойства полностью настраиваемый. Такие свойства не будут отображаться в People Hub, поскольку Windows Phone не знает, как с ними обращаться, но они могут использоваться вашим приложением.

В следующем примере мы добавляем новую пользовательскую информацию под названием MVP Category :

01
02
03
04
05
06
07
08
09
10
11
12
13
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
 
    StoredContact contact = new StoredContact(store);
    contact.GivenName = «Matteo»;
    contact.FamilyName = «Pagani»;
 
    IDictionary<string, object> extendedProperties = await contact.GetExtendedPropertiesAsync();
    extendedProperties.Add(«MVP Category», «Windows Phone Development»);
 
    await contact.SaveAsync();
}

Поиск контактов в личной книге контактов немного сложен, потому что нет прямого способа поиска контакта по определенному полю.

Поиск выполняется с использованием класса ContactQueryResult , который создается путем вызова метода CreateContactQuery() объекта ContactStore . Единственными доступными операциями являются GetContactsAsync() , которая возвращает все контакты, и GetContactCountAsync() , который возвращает количество доступных контактов.

Вы также можете заранее определить, с какими полями вы собираетесь работать, но вам все равно придется использовать метод GetPropertiesAsync() для извлечения правильных значений. Давайте посмотрим, как это работает в следующем примере, в котором мы ищем контакт, адрес электронной почты которого является [email protected] :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private async void OnSearchContactClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
    ContactQueryOptions options = new ContactQueryOptions();
    options.DesiredFields.Add(KnownContactProperties.Email);
 
    ContactQueryResult result = store.CreateContactQuery(options);
    IReadOnlyList<StoredContact> contactList = await result.GetContactsAsync();
 
    foreach (StoredContact contact in contactList)
    {
        IDictionary<string, object> properties = await contact.GetPropertiesAsync();
        if (properties.ContainsKey(KnownContactProperties.Email) &&
            properties[KnownContactProperties.Email].ToString() == «[email protected]»)
        {
            MessageBox.Show(«Contact found!»);
        }
    }
}

Вы можете определить, какие поля вас интересуют, создав новый объект ContactQueryOptions и добавив его в коллекцию DesiredFields . Затем вы можете передать объект ContactQueryOptions в качестве параметра при создании ContactQueryResult . Как видите, определения полей недостаточно для получения желаемого результата. Нам все еще нужно запросить каждый контакт, используя метод GetPropertiesAsync() чтобы узнать, является ли значение информации искомым.

Цель класса ContactQueryOptions — подготовить следующие операции запроса, чтобы они могли выполняться быстрее.

Обновление контакта осуществляется так же, как и создание нового: после того, как вы получили контакт, который хотите изменить, вам нужно изменить необходимую информацию и снова вызвать метод SaveAsync() , как в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
private async void OnSearchContactClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
    ContactQueryOptions options = new ContactQueryOptions();
    options.DesiredFields.Add(KnownContactProperties.Email);
 
    ContactQueryResult result = store.CreateContactQuery(options);
    IReadOnlyList<StoredContact> contactList = await result.GetContactsAsync();
 
    foreach (StoredContact contact in contactList)
    {
        IDictionary<string, object> properties = await contact.GetPropertiesAsync();
        if (properties.ContainsKey(KnownContactProperties.Email) &&
            properties[KnownContactProperties.Email].ToString() == «[email protected]»)
        {
            properties[KnownContactProperties.Email] = «[email protected]»;
            await contact.SaveAsync();
        }
    }
}

После того, как мы [email protected] пользователя, чей адрес электронной почты — [email protected] , мы изменили его на [email protected] и сохранили его.

Удаление работает аналогичным образом, за исключением того, что вам придется иметь дело с идентификатором контакта, который является уникальным идентификатором, который автоматически присваивается магазином (вы не можете установить его; вы можете только прочитать его). Получив контакт, который вы хотите удалить, вы должны вызвать метод ContactStore объекта ContactStore , передав в качестве параметра идентификатор контакта, который хранится в свойстве Id класса StoredContact .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private async void OnSearchContactClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
    ContactQueryOptions options = new ContactQueryOptions();
    options.DesiredFields.Add(KnownContactProperties.Email);
 
    ContactQueryResult result = store.CreateContactQuery(options);
    IReadOnlyList<StoredContact> contactList = await result.GetContactsAsync();
 
    foreach (StoredContact contact in contactList)
    {
        IDictionary<string, object> properties = await contact.GetPropertiesAsync();
        if (properties.ContainsKey(KnownContactProperties.Email) &&
            properties[KnownContactProperties.Email].ToString() == «[email protected]»)
        {
            await store.DeleteContactAsync(contact.Id);
        }
    }
}

В предыдущем примере, после того как мы [email protected] контакт с адресом электронной почты [email protected] , мы удаляем его, используя его уникальный идентификатор.

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

Для этого сценария класс StoredContact предлагает свойство RemoteId для хранения такой информации. Наличие RemoteId также упрощает операции поиска, которые мы видели ранее. ContactStore класс ContactStore предлагает метод FindContactByRemoteIdAsync() , который может извлекать определенный контакт на основе удаленного идентификатора, как показано в следующем примере:

01
02
03
04
05
06
07
08
09
10
private async void OnFindButtonClicked(object sender, RoutedEventArgs e)
{
    ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
 
    string myRemoteId = «2918»;
 
    RemoteIdHelper remoteHelper = new RemoteIdHelper();
    string taggedRemoteId = await remoteHelper.GetTaggedRemoteId(store, myRemoteId);
    StoredContact contact = await store.FindContactByRemoteIdAsync(taggedRemoteId);
}

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

В этой статье, опубликованной Microsoft, вы можете увидеть реализацию класса RemoteIdHelper который предлагает несколько методов для добавления случайной информации к удаленному идентификатору (с использованием GUID), чтобы убедиться, что он уникален.

Детский уголок — это интересная и инновационная функция, представленная в Windows Phone 8, которая особенно полезна для родителей маленьких детей. По сути, это песочница, которую мы можем настроить. Мы можем решить, какие приложения, игры, картинки, видео и музыка могут быть доступны.

Как разработчики, мы можем знать, когда приложение работает в режиме «Детский уголок». Таким образом, мы можем настроить взаимодействие с пользователем, чтобы избежать предоставления нежелательного контента, такого как обмен функциями.

Воспользоваться этой функцией легко; мы просто проверяем свойство Modes класса ApplicationProfile , который принадлежит пространству имен Windows.Phone.ApplicationModel . Когда установлено значение « Default , приложение работает нормально. Если он установлен на Alternate , он работает в режиме детского угла.

01
02
03
04
05
06
07
08
09
10
11
private void OnCheckStatusClicked(object sender, RoutedEventArgs e)
{
    if (ApplicationProfile.Modes == ApplicationProfileModes.Default)
    {
        MessageBox.Show(«The app is running in normal mode.»);
    }
    else
    {
        MessageBox.Show(«The app is running in Kid’s Corner mode.»);
    }
}
Начальный экран Детского уголка

Речевые API являются одной из самых интересных новых функций, добавленных в Windows Phone 8. С точки зрения пользователя, голосовые функции управляются на странице настроек. Раздел «Речь» позволяет пользователям настраивать все основные параметры, такие как тип голоса, но, прежде всего, он используется для настройки языка, который они хотят использовать для речевых служб. Как правило, он установлен на том же языке пользовательского интерфейса, и пользователи могут изменить его, загрузив и установив новый голосовой пакет. Важно понимать, как настроены речевые сервисы, потому что в вашем приложении вы сможете использовать распознавание речи только для языков, которые были установлены пользователем.

Целью речевых сервисов является добавление поддержки распознавания голоса в ваши приложения следующими способами:

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

В этом разделе мы рассмотрим основные требования для реализации всех трех режимов в вашем приложении.

Windows Phone предоставляет диалог для использования голосовых команд

Голосовые команды — это способ запустить ваше приложение и выполнить определенную задачу независимо от того, что делает пользователь. Они активируются нажатием и удержанием кнопки «Пуск». Windows Phone предлагает встроенную поддержку многих голосовых команд, таких как запуск телефонного звонка, диктовка электронной почты, поиск через Bing и многое другое.

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

Голосовые команды основаны на файлах VCD, которые являются файлами XML, которые включены в ваш проект. Используя специальный синтаксис, вы сможете определить все команды, которые вы хотите поддерживать в своем приложении, и то, как приложение должно вести себя, когда они используются. Эти файлы изначально поддерживаются Visual Studio. Если вы щелкнете правой кнопкой мыши по своему проекту и выберете Добавить новый элемент , вы найдете шаблон под названием VoiceCommandDefinition в разделе Windows Phone.

Следующий пример кода представляет собой файл VCD:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<?xml version=»1.0″ encoding=»utf-8″?>
 
<VoiceCommands xmlns=»http://schemas.microsoft.com/voicecommands/1.0″>
  <CommandSet xml:lang=»it» Name=»NotesCommandSet»>
    <CommandPrefix>My notes</CommandPrefix>
    <Example> Open my notes and add a new note </Example>
 
    <Command Name=»AddNote»>
      <Example> add a new note </Example>
      <ListenFor> [and] add [a] new note </ListenFor>
      <ListenFor> [and] create [a] new note </ListenFor>
      <Feedback> I’m adding a new note… </Feedback>
      <Navigate Target=»/AddNote.xaml» />
    </Command>
 
  </CommandSet>
</VoiceCommands>

Файл VCD может содержать один или несколько узлов CommandSet , которые идентифицируются по Name и определенному языку (атрибут xml:lang ). Второй атрибут является наиболее важным. Ваше приложение будет поддерживать голосовые команды только для языков, которые вы включили в CommandSet в файле VCD (язык голосовых команд определяется пользователями на странице настроек). Вы можете иметь несколько узлов CommandSet для поддержки нескольких языков.

Каждый CommandSet может иметь CommandPrefix — текст, который должен произносить пользователь, чтобы начать отправку команд нашему приложению. Если он не указан, будет автоматически использовано имя приложения. Это свойство полезно, если вы хотите локализовать команду или если заголовок вашего приложения слишком сложен для произнесения. Вы также можете добавить тег « Example , который содержит текст, отображаемый в диалоговом окне Windows Phone, чтобы помочь пользователям понять, какие команды они могут использовать.

Затем внутри CommandSet вы можете добавить до 100 команд, идентифицированных тегом Command . Каждая команда имеет следующие характеристики:

  • Уникальное имя, которое задается в атрибуте Name .
  • Тег Example показывает пользователям образец текста для текущей команды.
  • ListenFor содержит текст, который необходимо произнести, чтобы активировать команду. Для ListenFor можно указать до десяти тегов ListenFor чтобы охватить варианты текста. Вы также можете добавить дополнительные слова в квадратных скобках. В предыдущем примере команду AddNote можно активировать, произнеся «добавить новую заметку» или «и добавить новую заметку».
  • Feedback — это текст, который произносит Windows Phone, чтобы уведомить пользователей, что он понял команду и обрабатывает ее.
  • NavigateTarget можно использовать для настройки потока навигации приложения. Если мы не установим его, по умолчанию приложение откроется на главной странице. В противном случае, как и в предыдущем примере, мы можем перенаправить пользователя на определенную страницу. Конечно, в обоих случаях мы получим информацию о голосовой команде; мы увидим, как бороться с ними позже.

После того, как мы завершили определение VCD, мы готовы использовать его в нашем приложении.

Примечание. Чтобы использовать речевые службы, вам нужно включить опцию ID_CAP_SPEECH_RECOGNITION в файле манифеста.

Команды внедряются в приложение Windows Phone с помощью класса VoiceCommandService , который принадлежит пространству имен Windows.Phone.Speech.VoiceCommands . Этот статический класс предоставляет метод InstallCommandSetFromFileAsync() , который требует путь к только что созданному VCD-файлу.

1
2
3
4
private async void OnInitVoiceClicked(object sender, RoutedEventArgs e)
{
    await VoiceCommandService.InstallCommandSetsFromFileAsync(new Uri(«ms-appx:///VoiceCommands.xml»));
}

Путь к файлу выражается с помощью Uri который должен начинаться с префикса ms-appx:/// . Этот Uri относится к структуре проекта Visual Studio, начиная с корня.

Файл VCD также может содержать список фраз, как в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version=»1.0″ encoding=»utf-8″?>
 
<VoiceCommands xmlns=»http://schemas.microsoft.com/voicecommands/1.0″>
  <CommandSet xml:lang=»en» Name=»NotesCommandSet»>
    <CommandPrefix>My notes</CommandPrefix>
    <Example> Open my notes and add a new note </Example>
 
    <Command Name=»OpenNote»>
      <Example> open the note </Example>
      <ListenFor> open the note {number} </ListenFor>
      <Feedback> I’m opening the note… </Feedback>
      <Navigate />
    </Command>
 
    <PhraseList Label=»number»>
      <Item> 1 </Item>
      <Item> 2 </Item>
      <Item> 3 </Item>
    </PhraseList>
 
  </CommandSet>
</VoiceCommands>

Списки фраз используются для управления параметрами, которые могут быть добавлены к фразе с помощью фигурных скобок. Каждый узел PhraseList идентифицируется атрибутом Label , который является ключевым словом для включения в фигурные скобки внутри узла ListenFor . В предыдущем примере пользователи могут произносить фразу «открыть заметку», за которой следует любое из чисел, указанных с помощью тега Item в PhraseList . Вы можете иметь до 2000 элементов в одном списке.

Предыдущий пример полезен для понимания того, как работает эта функция, но он не очень реалистичен; часто список параметров не является статичным, но динамически обновляется во время выполнения приложения. Возьмем предыдущий сценарий в качестве примера: в приложении заметок список заметок не является фиксированным, поскольку пользователи могут создавать неограниченное количество заметок.

API предлагают способ динамически обновлять PhraseList , как показано в следующем примере:

1
2
3
4
5
private async void OnUpdatePhraseListClicked(object sender, RoutedEventArgs e)
{
    VoiceCommandSet commandSet = VoiceCommandService.InstalledCommandSets[«NotesCommandSet»];
    await commandSet.UpdatePhraseListAsync(«number», new string[] { «1», «2», «3», «4», «5» });
}

Сначала вы должны получить ссылку на текущий набор команд с помощью коллекции VoiceCommandService.InstalledCommandSets . В качестве индекса вы должны использовать имя набора, который вы определили в файле VCD (атрибут Name тега CommandSet ). Получив ссылку на набор, вы можете вызвать UpdatePhraseListAsync() чтобы обновить список, передав два параметра:

  • имя PhraseList (задается с помощью атрибута Label )
  • коллекция новинок, как массив строк

Важно помнить, что метод UpdatePhraseListAsync() переопределяет текущие элементы в PhraseList , поэтому вам придется каждый раз добавлять все доступные элементы, а не только новые.

Команда, вызванная пользователем, отправляется в ваше приложение с механизмом строки запроса, описанным ранее в этой серии . Когда приложение открывается командой, пользователь перенаправляется на страницу, указанную в узле Navigate файла VCD. Ниже приведен пример URI:

1
/MainPage.xaml?voiceCommandName=AddNote&reco=My%20notes%20create%20a%20new%20note

Параметр voiceCommandName содержит voiceCommandName команду, а параметр reco содержит полный текст, который был распознан Windows Phone.

Если команда поддерживает список фраз, вы получите другой параметр с тем же именем PhraseList и произносимым элементом в качестве значения.Следующий код представляет собой пример URI на основе предыдущего примера заметки, где пользователь может открыть конкретную заметку с помощью OpenNoteкоманды:

1
/MainPage.xaml?voiceCommandName=OpenNote&reco=My%20notes%20open%20a%20new%20note&number=2

Используя API, которые мы видели ранее в этой серии, легко извлечь необходимую информацию из параметров строки запроса и использовать их для наших целей, как в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (NavigationContext.QueryString.ContainsKey("voiceCommandName"))
    {
        string commandName = NavigationContext.QueryString["voiceCommandName"];
 
        switch (commandName)
        {
            case "AddNote":
                //Create a new note.
                break;
            case "OpenNote":
                if (NavigationContext.QueryString.ContainsKey("number"))
                {
                    int selectedNote = int.Parse(NavigationContext.QueryString["number"]);
                    //Load the selected note.
                }
                break;
        }
    }
}

Мы используем switchоператор для управления различными поддерживаемыми командами, которые доступны в NavigationContext.QueryStringколлекции. Если пользователь пытается открыть заметку, мы также получаем значение numberпараметра.

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

Есть два способа начать распознавание речи: предоставить пользовательский интерфейс или работать в фоновом режиме.

В первом случае вы можете предоставить пользователям визуальный диалог, аналогичный тому, который используется операционной системой при удерживании кнопки «Пуск». Это идеальное решение для управления голосовыми командами, потому что вы сможете предоставлять пользователям визуальную и голосовую обратную связь.

Это достигается с помощью SpeechRecognizerUIкласса, который предлагает четыре ключевых свойства для настройки визуального диалога:

  • ListenText это большой, жирный текст, который объясняет пользователям, что ожидает приложение.
  • ExampleЭто дополнительный текст, который отображается ниже, ListenTextчтобы помочь пользователям лучше понять, какую речь ожидает приложение.
  • ReadoutEnabledявляется Booleanсобственностью; если установлено значение true, Windows Phone будет читать распознанный текст пользователям в качестве подтверждения.
  • ShowConfirmationдругая Booleanсобственность; если для этого параметра установлено значение true, пользователи смогут отменить операцию после завершения процесса распознавания.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private async void OnStartRecordingClicked(object sender, RoutedEventArgs e)
{
    SpeechRecognizerUI sr = new SpeechRecognizerUI();
    sr.Settings.ListenText = "Start dictating the note";
    sr.Settings.ExampleText = "dictate the note";
    sr.Settings.ReadoutEnabled = false;
    sr.Settings.ShowConfirmation = true;
 
    SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();
    if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
    {
        RecordedText.Text = result.RecognitionResult.Text;
    }
}

Обратите внимание, как начинается процесс распознавания, вызывая RecognizeWithUIAsync()метод, который возвращает SpeechRecognitionUIResultобъект, который содержит всю информацию об операции.

Для автоматического распознавания текста требуется меньше кода, поскольку доступно меньше параметров, чем было использовано для диалога. Нам просто нужно начать слушать текст и понять его. Мы можем сделать это, вызвав RecognizeAsync()метод SpeechRecognizerкласса. Результат распознавания будет сохранен в SpeechRecognitionResultобъекте, который является тем же, который был возвращен в RecognitionResultсвойстве RecognizeWithUIAsync()методом, который мы использовали ранее.

Код, который мы видели до сих пор, используется для распознавания практически любого слова в словаре. По этой причине речевые службы будут работать только в том случае, если телефон подключен к Интернету, поскольку эта функция использует онлайновые службы Microsoft для анализа результатов.

Этот подход полезен, когда пользователи говорят с приложением, чтобы диктовать текст, как в предыдущих примерах с приложением заметки. Но если нужно управлять только несколькими командами, доступ ко всем словам в словаре не требуется. Напротив, полный доступ может вызвать проблемы, потому что приложение может понимать слова, которые не связаны ни с одной из поддерживаемых команд.

Для этого сценария API речи предоставляют возможность использовать пользовательскую грамматику и ограничивают количество слов, которые поддерживаются в процессе распознавания. Есть три способа установить пользовательскую грамматику:

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

Опять же, отправной точкой является SpeechRecognizerкласс, который предлагает свойство с именем Grammars.

Чтобы загрузить одну из предопределенных грамматик, используйте AddGrammarFromPredefinedType()метод, который принимает в качестве параметров строку для ее идентификации (вы можете выбрать любое значение) и тип используемой грамматики. Существует два набора грамматик: стандартная SpeechPredefinedGrammar.Dictationи SpeechPredefinedGrammar.WebSearchоптимизированная для веб-задач.

В следующем примере мы распознаем речь с помощью WebSearchграмматики:

1
2
3
4
5
6
7
private async void OnStartRecordingWithoutUIClicked(object sender, RoutedEventArgs e)
{
    SpeechRecognizer recognizer = new SpeechRecognizer();
    recognizer.Grammars.AddGrammarFromPredefinedType("WebSearch", SpeechPredefinedGrammar.WebSearch);
    SpeechRecognitionResult result = await recognizer.RecognizeAsync();
    RecordedText.Text = result.Text;
}

Еще более полезной является возможность позволить процессу распознавания понимать только несколько выбранных слов. Мы можем использовать AddGrammarFromList()метод, предлагаемый Grammarsсвойством, который требует обычного идентификационного ключа, за которым следует набор поддерживаемых слов.

В следующем примере мы установили для SpeechRecognizerкласса понимание только слов «сохранить» и «отменить».

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
private async void OnStartRecordingClicked(object sender, RoutedEventArgs e)
{
    SpeechRecognizer recognizer = new SpeechRecognizer();
    string[] commands = new[] { "save", "cancel" };
    recognizer.Grammars.AddGrammarFromList("customCommands", commands);
 
    SpeechRecognitionResult result = await recognizer.RecognizeAsync();
    if (result.Text == "save")
    {
        //Saving
    }
    else if (result.Text == "cancel")
    {
        //Cancelling the operation
    }
    else
    {
        MessageBox.Show("Command not recognized");
    }
}

Если пользователь произносит слово, которое не включено в пользовательскую грамматику, Textсвойство SpeechRecognitionResultобъекта будет пустым. Самым большим преимуществом этого подхода является то, что он не требует подключения к Интернету, поскольку грамматика хранится локально.

Третий и последний способ загрузки грамматики — использование другого определения XML Speech Recognition Grammar Specification (SRGS). Вы можете прочитать больше о поддерживаемых тегах в официальной документации W3C.

В следующем примере показан файл пользовательской грамматики:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8" ?>
 
<grammar version="1.0" xml:lang="en" root="rootRule" tag-format="semantics/1.0"
         xmlns="http://www.w3.org/2001/06/grammar"
 
  <rule id="openAction">
    <one-of>
      <item>open</item>
      <item>load</item>
    </one-of>
  </rule>
 
  <rule id="fileWords">
    <one-of>
      <item> note </item>
      <item> reminder </item>
    </one-of>
  </rule>
 
  <rule id="rootRule">
    <ruleref uri="#openAction" />
    <one-of>
      <item>the</item>
      <item>a</item>
    </one-of>
    <ruleref uri="#fileWords" />
  </rule>
 
</grammar>

Файл описывает как поддерживаемые слова, так и правильный порядок, который следует использовать. В предыдущем примере показаны поддерживаемые команды для управления заметками в приложении, например «Открыть заметку» или «Загрузить напоминание», в то время как такая команда, как «Напоминание открыть», не распознается.

Visual Studio 2012 предлагает встроенную поддержку для этих файлов с определенным шаблоном, SRGS Grammarкоторый доступен, когда вы щелкаете правой кнопкой мыши по своему проекту и выбираете Добавить новый элемент .

Как только файл станет частью вашего проекта, вы можете загрузить его, используя AddGrammarFromUri()метод SpeechRecognizerкласса, который принимает в качестве параметра путь к файлу, выраженный как Uri, точно так же, как мы видели для файлов VCD. Отныне процесс распознавания будет использовать грамматику, определенную в файле, вместо стандартной, как показано в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private async void OnStartRecordingWithCustomFile(object sender, RoutedEventArgs e)
{
    SpeechRecognizer recognizer = new SpeechRecognizer();
    recognizer.Grammars.AddGrammarFromUri("CustomGrammar", new Uri("ms-appx:///CustomGrammar.xml"));
    SpeechRecognitionResult result = await recognizer.RecognizeAsync();
    if (result.Text != string.Empty)
    {
        RecordedText.Text = result.Text;
    }
    else
    {
        MessageBox.Show("Not recognized");
    }
}

Преобразование текста в речь — это технология, которая способна читать текст пользователям синтезированным голосом. Его можно использовать для создания диалога с пользователями, чтобы им не приходилось смотреть на экран для взаимодействия с приложением.

Основное использование этой функции действительно просто. Базовый класс для взаимодействия со службами TTS — SpeechSynthesizerэто метод, который называется SpeakTextAsync(). Вам просто нужно передать методу текст, который вы хотите прочитать, как показано в следующем примере:

1
2
3
4
5
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
    SpeechSynthesizer synth = new SpeechSynthesizer();
    await synth.SpeakTextAsync("This is a sample text");
}

Кроме того, можно настроить способ произношения текста с помощью стандартного языка под названием Synthesis Markup Language (SSML) , который основан на стандарте XML. Этот стандарт предоставляет серию тегов XML, которые определяют, как слово или часть текста должны произноситься. Например, можно изменить скорость, язык, пол голоса и многое другое.

В следующем примере приведен пример файла SSML:

01
02
03
04
05
06
07
08
09
10
11
<?xml version=»1.0″?>
       xmlns:dc=»http://purl.org/dc/elements/1.1/»
       xml:lang="en"
       version="1.0">
 
  <voice age="5">This text is read by a child</voice>
  <break />
  <prosody rate="x-slow"> This text is read very slowly</prosody>
   
</speak>

Этот код содержит три примера тегов SSML: voiceдля имитации возраста голоса, breakдля добавления паузы и prosodyдля установки скорости чтения с помощью rateатрибута.

Есть два способа использовать определение SSML в вашем приложении. Первый — создать внешний файл, добавив новый XML-файл в ваш проект. Затем вы можете загрузить его, передав путь к файлу SpeakSsmlFromUriAsync()методу SpeechSynthesizerкласса, аналогично тому, как мы загружали файл VCD.

1
2
3
4
5
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
    SpeechSynthesizer synth = new SpeechSynthesizer();
    await synth.SpeakSsmlFromUriAsync(new Uri("ms-appx:///SSML.xml"));
}

Другой способ — определить текст, который будет читаться непосредственно в коде, создав строку, содержащую теги SSML. В этом случае мы можем использовать SpeakSsmlAsync()метод, который принимает строку для чтения в качестве параметра. В следующем примере показано то же определение SSML, которое мы использовали, но оно хранится в строке вместо внешнего файла.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
    SpeechSynthesizer synth = new SpeechSynthesizer();
 
    StringBuilder textToRead = new StringBuilder();
    textToRead.AppendLine("<speak version=\"1.0\"");
    textToRead.AppendLine(" xmlns=\"http://www.w3.org/2001/10/synthesis\"");
    textToRead.AppendLine(" xml:lang=\"en\">");
    textToRead.AppendLine(" <voice age=\"5\">This text is read by a child</voice>");
    textToRead.AppendLine("<prosody rate=\"x-slow\"> This text is read very slowly</prosody>");
    textToRead.AppendLine("</speak>");
 
    await synth.SpeakSsmlAsync(textToRead.ToString());
}

Вы можете узнать больше об определении SSML и доступных тегах в официальной документации, предоставленной W3C.

Совместное использование данных — это новая функция, представленная в Windows Phone 8, которая может использоваться для обмена данными между различными приложениями, в том числе сторонними.

Существует два способа управления совместным использованием данных:

  • Общий доступ к файлам : приложение регистрирует расширение, такое как .log. Он сможет управлять любым файлом с зарегистрированным расширением, открытым другим приложением (например, почтовым вложением).
  • Совместное использование протокола : приложение регистрирует протокол, такой как log:. Другие приложения смогут использовать его для отправки простых данных, таких как строки или числа.

В обоих случаях пользовательский опыт похож:

  • Если на устройстве нет приложения для управления запрошенным расширением или протоколом, пользователям будет задан вопрос, хотят ли они найти в Магазине то, которое может.
  • Если для запрошенного расширения или протокола зарегистрировано только одно приложение, оно будет автоматически открыто.
  • Если для одного расширения или протокола зарегистрировано несколько приложений, пользователи смогут выбрать, какое из них использовать.
Обмен данными, когда совместимое приложение отсутствует на левом устройстве, а несколько приложений совместимо справа

Давайте обсудим, как поддержать оба сценария в нашем приложении.

Примечание. Существует несколько типов файлов и протоколов, которые регистрируются системой, например файлы Office, изображения, почтовые протоколы и т. Д. Вы не можете их переопределить; только Windows Phone может управлять ими. Вы можете увидеть полный список зарезервированных типов в документации MSDN .

Совместное использование файлов поддерживается добавлением нового определения в файл манифеста, который уведомляет операционную систему о том, какими расширениями может управлять приложение. Как и во многих других сценариях, которые мы видели ранее, эта модификация не поддерживается визуальным редактором, поэтому нам нужно щелкнуть правой кнопкой мыши файл манифеста и выбрать параметр « Просмотреть код» .

Расширение добавлено в Extensionsраздел, который должен быть определен под Tokenодним:

01
02
03
04
05
06
07
08
09
10
11
12
<Extensions>
  <FileTypeAssociation Name="LogFile" TaskID="_default" NavUriFragment="fileToken=%s">
    <Logos>
      <Logo Size="small">log-33x33.png</Logo>
      <Logo Size="medium">log-69x69.png</Logo>
      <Logo Size="large">log-176x176.png</Logo>
    </Logos>
    <SupportedFileTypes>
      <FileType ContentType="text/plain">.log</FileType>
    </SupportedFileTypes>
  </FileTypeAssociation>
</Extensions>

Каждый поддерживаемый тип файла имеет свой собственный FileTypeAssociationтег, который идентифицируется Nameатрибутом (который должен быть уникальным). Внутри этого узла находятся два вложенных раздела:

  • Logosне является обязательным и используется для поддержки значка для визуальной идентификации типа файла. Требуются три разных изображения, каждое с разным разрешением: 33 × 33, 69 × 69 и 176 × 176. Значки используются в различных контекстах, например, когда файл принимается в качестве вложения электронной почты.
  • SupportedFileTypesтребуется, потому что он содержит расширения, которые будут поддерживаться для текущего типа файла. Можно добавить несколько расширений.

Предыдущий пример используется для управления .logрасширением файла в нашем приложении.

Когда другое приложение пытается открыть файл, который мы поддерживаем, наше приложение открывается с помощью специального URI:

1
/FileTypeAssociation?fileToken=89819279-4fe0-9f57-d633f0949a19

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

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

Следующий пример показывает, как UriMapperвыглядит:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UriMapper: UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        string tempUri = HttpUtility.UrlDecode(uri.ToString());
        if (tempUri.Contains("/FileTypeAssociation"))
        {
            int fileIdIndex = tempUri.IndexOf("fileToken=") + 10;
            string fileId = tempUri.Substring(fileIdIndex);
 
            string incomingFileName =
                SharedStorageAccessManager.GetSharedFileName(fileId);
 
            string incomingFileType = System.IO.Path.GetExtension(incomingFileName);
 
            switch (incomingFileType)
            {
                case ".log":
                    return new Uri("/LogPage.xaml?fileToken=" + fileId, UriKind.Relative);
                default:
                    return new Uri("/MainPage.xaml", UriKind.Relative);
            }
        }
 
        return uri;
    }
}

Если в начале Uriсодержится FileTypeAssociationключевое слово, это означает, что приложение было открыто из-за запроса на обмен файлами. В этом случае нам нужно определить расширение открытого файла. Мы извлечь fileTokenпараметр и, используя GetSharedFileName()в SharedAccessManagerклассе (который принадлежит к Windows.Phone.Storageпространству имен), мы получаем исходное имя файла.

Читая имя, мы можем определить расширение и выполнить соответствующее перенаправление. В предыдущем примере, если расширение есть .log, мы перенаправляем пользователя на определенную страницу приложения LogPage.xaml. Очень важно , чтобы добавить к Uriв fileTokenпараметре в виде строки запроса; мы собираемся использовать его на странице для эффективного извлечения файла. Не забудьте зарегистрировать UriMapperв App.xaml.csфайле, как описано ранее в этой серии.

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

Теперь пришло время взаимодействовать с файлом, который мы получили из другого приложения. Мы сделаем это на странице, которую мы создали для этой цели (в предыдущем примере кода она называется LogPage.xaml).

Мы видели, что когда другое приложение пытается открыть .logфайл, пользователь перенаправляется на LogPage.xamlстраницу с fileTokenпараметром, добавленным в строку запроса. Мы собираемся использовать OnNavigatedToсобытие для управления этим сценарием:

01
02
03
04
05
06
07
08
09
10
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    if (NavigationContext.QueryString.ContainsKey(“fileToken”))
    {
        await SharedStorageAccessManager.CopySharedFileAsync(ApplicationData.Current.LocalFolder, “file.log”,
                                                                NameCollisionOption.ReplaceExisting,
                                                                NavigationContext.QueryString[“fileToken”]);
 
    }
}

Мы снова используем SharedStorageAccessManagerкласс, на этот раз вызывая CopySharedFileAsync()метод. Его цель — скопировать полученный файл в локальное хранилище, чтобы мы могли с ним работать.

Обязательные параметры:

  • StorageFolderОбъект, который представляет собой локальную папку хранения , в котором необходимо сохранить файл (в предыдущем примере, мы сохраняем его в корне).
  • Название файла.
  • Поведение, применяемое в случае, если файл с таким именем уже существует (с использованием одного из значений NameCollisionOptionперечислителя).
  • GUID, который идентифицирует файл, который мы получаем из fileTokenпараметра строки запроса.

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

До сих пор мы видели, как управлять открытым файлом в нашем приложении, но нам еще предстоит обсудить, как эффективно открыть файл.

Задача легко решается с помощью LaunchFileAsync()метода, предлагаемого Launcherклассом (который принадлежит Windows.Systemпространству имен). В StorageFileкачестве параметра требуется объект, представляющий файл, который вы хотите открыть.

В следующем примере вы можете увидеть, как открыть файл журнала, включенный в проект Visual Studio:

1
2
3
4
5
private async void OnOpenFileClicked(object sender, RoutedEventArgs e)
{
    StorageFile storageFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(“file.log”);
    Windows.System.Launcher.LaunchFileAsync(storageFile);
}

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

Начнем с манифеста.Также и в этом случае нам придется добавить новый элемент в Extensionsраздел, к которому можно получить доступ, отредактировав файл вручную с помощью этой View codeопции.

1
2
3
<Extensions>
  <Protocol Name=“log” NavUriFragment=“encodedLaunchUri=%s” TaskID=“_default” />
</Extensions>

Самый важный атрибут Name, который определяет протокол, который мы собираемся поддерживать. Два других атрибута исправлены.

Приложение, которое поддерживает совместное использование протокола, открывается со следующим URI:

1
/Protocol?encodedLaunchUri=log:ShowLog?LogId=1

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class UriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        string tempUri = System.Net.HttpUtility.UrlDecode(uri.ToString());
 
        if (tempUri.Contains(“Protocol”))
        {
            int logIdIndex = tempUri.IndexOf(“LogId=“) + 6;
            string logId = tempUri.Substring(logIdIndex);
 
            return new Uri(“/LogPage.xaml?LogId=“ + logId, UriKind.Relative);
        }
 
        return uri;
    }
}

В этом случае операция проще. Мы извлекаем значение параметра LogIdи передаем его на LogPage.xamlстраницу. Кроме того, у нас меньше работы на целевой странице; нам просто нужно извлечь значение параметра, используя OnNavigatedToсобытие, и использовать его для загрузки необходимых данных, как показано в следующем примере:

1
2
3
4
5
6
7
8
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (NavigationContext.QueryString.ContainsKey(“LogId”))
    {
        string logId = NavigationContext.QueryString[“LogId”];
        MessageBox.Show(logId);
    }
}

Подобно обмену файлами, другие приложения могут взаимодействовать с нашими, используя функцию общего доступа к протоколу и Launcherкласс, принадлежащий Windows.Systemпространству имен.

Разница в том, что нам нужно использовать LaunchUriAsync()метод, как показано в следующем примере:

1
2
3
4
5
private async void OnOpenUriClicked(object sender, RoutedEventArgs e)
{
    Uri uri = new Uri(“log:ShowLog?LogId=1”);
    await Windows.System.Launcher.LaunchUriAsync(uri);
}

В этом руководстве мы рассмотрели различные способы интеграции нашего приложения с функциями, предлагаемыми платформой Windows Phone:

  • Мы начали с простейшей интеграции: средства запуска и выбора, которые используются для запроса операции от операционной системы и, в конечном итоге, для получения некоторых данных.
  • Мы рассмотрели, как взаимодействовать с контактами и встречами пользователей: сначала в режиме «только чтение», предлагаемом новым набором API-интерфейсов, представленным в Windows Phone 7.5, а затем в личной книге контактов, которая является хранилищем контактов, принадлежащим приложению. но можно интегрировать с родным People Hub.
  • Мы вкратце поговорили о том, как воспользоваться преимуществами Kid’s Corner — инновационной функции, которая позволяет детям безопасно пользоваться телефоном без доступа к приложениям, которые им не подходят.
  • Мы узнали, как использовать один из самых мощных новых API, добавленных в Windows Phone 8: Speech API, для взаимодействия с нашим приложением с помощью голосовых команд.
  • Мы ввели обмен данными, который является еще одной новой функцией, используемой для обмена данными между различными приложениями, и мы можем управлять расширениями файлов и протоколами.

Это руководство представляет собой главу из Windows Phone 8 Succinctly , бесплатной электронной книги от команды Syncfusion.