Блог Сергея Байдачного

Мой блог о технологиях

Posts Tagged ‘Windows Phone 8

Распознавание и обработка речи (часть 3)

with one comment

Последняя, но немаловажная часть Speech API – распознавание речи. Тут Windows Phone 8 позволяет выполнять преобразование на основании имеющегося словаря, который может быть предоставлен как самой системой, так и разработчиком. При этом реализация приложения не привязана к вызову стандартных диалоговых окон, а может иметь и свой собственный интерфейс, позволяющий пользователю осуществить ввод.

Перейдем к более детальному описанию возможностей. Итак, как и в случае создания голосовых команд, первым делом нужно задекларировать несколько возможностей, это ID_CAP_NETWORKING, ID_CAP_MICROPHONE, ID_CAP_SPEECH_RECOGNITION:

<Capabilities>
  <Capability Name="ID_CAP_NETWORKING" />
  <Capability Name="ID_CAP_MICROPHONE" />
  <Capability Name="ID_CAP_SPEECH_RECOGNITION" />
</Capabilities>

Как только манифест модифицирован, можно перейти к реализации логики. Тут первым делом нужно определиться с языком, на котором говорит пользователь и проверить его наличие в системе. Для этого пригодиться класс InstalledSpeechRecognizers, который содержит два свойства, это All и Default, содержащие информацию о всех установленных механизмах распознавания и о механизме по умолчанию. Если Ваше приложение рассчитано на один из языков, то тут можно обойтись вот таким кодом, который будет проверять наличие языка и получать информацию о соответствующем механизме, чтобы в дальнейшем ее использовать:

SpeechRecognizerInformation info = null;

if ((from item in InstalledSpeechRecognizers.All
     where item.Language.Contains("ru")
     select item).Count() > 0)
{
    info = (from item in InstalledSpeechRecognizers.All
            where item.Language.Contains("ru")
            select item).First();
}

 

Как только мы убедились в том, что язык существует и доступен, можно переходить к инициализации объектов по распознаванию речи. Windows Phone 8 предлагает два класса, которые могут реализовать сколь угодно сложные сценарии, это SpeechRecognizerUI и SpeechRecognizer. Первый класс представляет собой готовое решение, поддерживающее стандартное диалоговое окно и возможности по его модификации. Однако, если Вы хотите разработать свой собственный интерфейс, то тут может быть полезен второй класс, который берет на себя лишь функцию распознавания речи. Создание объектов обеих классов не вызывает затруднений. Тут просто нужно помнить о том, что SpeechRecognizerUI является контейнером для SpeechRecognizer, поэтому доступ к свойствам механизма распознавания в случае доступен SpeechRecognizer напрямую, а в случае SpeechRecognizerUI нужно пользоваться свойством Recognizer.

SpeechRecognizer reco = new SpeechRecognizer();
reco.SetRecognizer(info);

SpeechRecognizerUI recoUI = new SpeechRecognizerUI();
recoUI.Recognizer.SetRecognizer(info);

 

Код выше не только создает объекты, но и задает выбранный нами язык.

Теперь поговорим немного о настройках самих объектов. Тут оба имеют свойство Settings, но в случае со SpeechRecognizer это доступ к настройкам самого механизма распознавания, а в случае со SpeechRecognizerUI – доступ к настройкам диалогового окна. В любом случае настройки достаточно простые. Если говорить о настройках стандартного диалогового окна, то тут следующие свойства:

· ExampleText – строка текста, которая задает пример того, что может сказать пользователь;

· ListenText – строка текста, задающая заголовок стандартного окна по распознаванию речи;

· ReadoutEnabled – позволяет определить, нужно ли зачитывать пользователю распознанный текст;

· ShowConfirmation – позволяет определить, нужно ли отображать подтверждение о том, что текст распознан (или нет), или это будет брать на себя само приложение.

В случае с объектом типа SpeachRecognizer, тут есть следующие свойства:

· BabbleTimeout – свойство задает время, которое пользователь может болтатьJ;

· EndSilenceTimeout – задает время, которое будет затрачено на распознавание речи после окончания монолога пользователя;

· InitialSilenceTimeout – задает время ожидания начала монолога пользователя.

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

Наконец, чтобы приступить к распознаванию, нужно определиться со списком доступных слов. Тут есть три варианта:

· Положится на тот словарь, который идет в стандартной поставке;

· Задать свой словарь с помощью простого списка;

· Использовать Speech Recognition Grammar Specification (SRGS), чтобы задать словарь в виде XML документа.

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

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

SpeechRecognitionUIResult recoResult = await recoUI.RecognizeWithUIAsync(); 

 

Если необходимо использовать механизм поиска в Web, то тут на одну строчку кода больше:

recoUI.Recognizer.Grammars.AddGrammarFromPredefinedType(
    "searchKey",SpeechPredefinedGrammar.WebSearch);

SpeechRecognitionUIResult recoResult = await recoUI.RecognizeWithUIAsync();

 

Как видно из кода, для вызова диалога по распознаванию речи используется метод RecognizeWithUIAsync. В случае использование класса SpeechRecognizer, используется метод RecognizeAsync. В качестве возвращаемого значения используется класс SpeechRecognitionUIResult (или SpeechRecognitionResult), откуда можно выбрать сам текст, который был распознан и его рейтинг (насколько верно был распознан текст в рамках текущего словаря). Рейтинг текста может быть одним из значений перечислимого типа SpeechRecognitionConfidence и принимать одно из четырех значений: High, Low, Medium и Rejected.

Наибольший интерес представляет возможность задания собственного словаря (особенно в свете того, что у меня русский словарь п умолчанию не работаетJ). Задания собственного словаря позволит улучшить точность распознавания в рамках Вашей предметной области. Кроме того, если словарь у Вас находится локально, то это позволит увеличить и скорость распознавания.

Для задания своего словаря используется коллекция Grammars объекта типа SpeechRecognizer. Поскольку Grammars коллекция, то способна содержать сразу набор словарей, но это могут быть словари на основе списка слов или на основе SRGS. Нельзя мешать собственный словари и словарь по умолчанию. Чтобы задать словарь в виде списка, достаточно использовать конструкцию наподобие этой:

reco.Grammars.AddGrammarFromList("myGr1",
    new List<String>() { "апельсин","яблоко","банан"});

 

Если словарь задается с помощью XML, то тут используется метод AddGrammarFromUri:

reco.Grammars.AddGrammarFromUri("myDict",
    new Uri("ms-appx:///myDict.grxml",UriKind.Absolute));

 

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

Вот собственно и все, что нужно знать о распознавании речи в Windows Phone 8.

Реклама

Written by Sergiy Baydachnyy

04.01.2013 at 12:41

Опубликовано в Windows Phone

Tagged with

Распознавание и обработка речи (часть 2)

leave a comment »

Преобразование текста в речь

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

<Capabilities>
  <Capability Name="ID_CAP_SPEECH_RECOGNITION" />
</Capabilities>

 

Теперь можно переходить к синтезу голоса. Для этого воспользуемся пространством имен Windows.Phone.Speech.Synthesis, которое содержит всего несколько полезных классов:

· InstalledVoices – этот класс содержит всего два свойства (All и Default) и позволяет предоставить информацию об установленных языках на телефоне пользователя. Тут программист может проверить, есть ли требуемый язык и подобрать тот, который подходит для данной ситуации, в противном случае система возьмет язык по умолчанию;

· VoiceInformation – этот класс содержит информацию о голосе. Тут содержится язык, пол синтезатора (как ни странно звучит). Используется совместно с InstalledVoices при обращении к свойству Default или переборе всей коллекции доступных языков в All. Сам по себе объект класса создать нельзя;

· SpeechSynthesizer – вот это и есть самый интересный класс, который выполняет преобразование текста в речь.

Фактически, чтобы преобразовать текст в речь, достаточно создать объект класса SpeechSynthesizer и вызвать метод SpeakTextAsync. Как видно из названия метода, он является асинхронным и поддерживает подход вызова через async/await. Естественно, чтобы вызвать метод, желательно проверить наличие нужного Вам языка, а еще лучше, установить его в качестве текущего с помощью метода SetVoice. Код ниже проверяет наличие русского языка, а в случае его присутствия, произносит фразу фразу «Привет», используя женский и мужской голос последовательно:

if ((from a in InstalledVoices.All 
     where a.Language.Contains("ru") 
     select a).Count() > 0)
{
    SpeechSynthesizer sr = new SpeechSynthesizer();
    sr.SetVoice((from a in InstalledVoices.All 
                 where a.Language.Contains("ru") 
                 select a).First());
    await sr.SpeakTextAsync("Привет");
    sr.SetVoice((from a in InstalledVoices.All 
                 where a.Language.Contains("ru") 
                 select a).Last());
    await sr.SpeakTextAsync("Привет");
}

 

Естественно, что преобразование текста в речь на основе простого текста не очень интересно, так как часто нужно использовать женский и мужской голос в одном абзаце, использовать разные языки, отправлять приложению событие при завершении произнесения части фразы и др. Все это можно делать, если использовать не простой текст, а набор инструкций, описанных с помощью специального языка Speech Synthesis Markup Language (SSML). Традиционно SSML базируется на XML и поддерживается в классе SpeechSynthesizer двумя методами: SpeakSsmlAsync и SpeakSsmlFromUriAsync. Первый метод принимает в качестве параметров строку, содержащую SSML, а второй – путь к файлу с соответствующими инструкциями.

Рассмотрим все элементы, из которых может состоять SSML документ:

· audio – позволяет вставить заранее подготовленный аудио файл. Это позволяет снабдить монолог дополнительными аудио эффектами. В качестве атрибута используется src – путь к файлу;

· break – позволяет задавать паузу. Для этого используется атрибут duration, который задает паузу в секундах или миллисекундах или атрибут strength, принимающий одно из предопределенных значений;

· emphasis – теоретически этот элемент должен позволять произносить текст с разным уровнем интонации, но это в Windows Phone 8 пока не реализовано;

· lexicon – этот элемент позволяет задать разработчику собственный лексикон, базируясь на специально созданной для этого спецификации Pronunciation Lexicon Specification (PLS). Это необходимо для сложных приложений, где необходимо четко задать набор слов и правильное их произношение;

· mark – позволяет задать метку, используя атрибут name. При достижении метки в приложении генерируется событие BookmarkReached. Это позволяет легко синхронизировать речь с происходящим на экране.

· p и s – позволяют задать параграф или высказывание. Элементы не обязательные, но имеют важный атрибут xml:lang, который позволяет переустановить язык;

· phoneme – с помощью этого элемента можно задать специальное произношение для заданной группы символов. Очень часто это касается имен;

· prosody – содержит много атрибутов, но пока поддерживается только volume, который позволяет задать громкость от 0 до 100;

· sayas – позволяет задать произношения для дат, времени, числительных и др. Очень полезный элемент, учитывая то, что те же даты пишутся по разному;

· speak – задает коневой элемент для всего SSML документа;

· sub – позволяет задать развернутое написание для аббревиатур;

· voice – позволяет задать голос, указав мужской или женский, язык и другие параметры.

Если Вы создаете файл с расширением ssml, то Visual Studio автоматически включает систему IntelliSense, что очень удобно. Пример простого ssml файла:

<speak version="1.0" 
       xmlns="http://www.w3.org/2001/10/synthesis" 
       xml:lang="ru-RU">
  <s xml:lang="ru">Чтобы сказать добрый день по английски, произнесите</s>
  <s xml:lang="en">Good morning</s>
</speak>

 

Код, который может открыть этот файл может выглядеть вот так:

SpeechSynthesizer sr = new SpeechSynthesizer();
string path = Package.Current.InstalledLocation.Path + "\\text.ssml";
Uri url = new Uri(path, UriKind.Absolute);
await sr.SpeakSsmlFromUriAsync(url);

 

Не забудьте проверить, что в свойствах файла указано Build Action = Content. По умолчанию это не происходит и Вы получите исключение о том, что файл не найден.

Written by Sergiy Baydachnyy

20.12.2012 at 22:03

Опубликовано в Windows Phone

Tagged with

Конкурс для Windows Phone разработчиков в Украине

2 комментария

Вспомнил, что не писал о таком замечательном конкурсе: http://wp8.promorc.com, который мы сейчас проводим для всех разработчиков и студентов в Украине.

На конкурсе 4 номинации:

· Лучшее приложение (тут все, что угодно – бизнес, социалка и др.)

· Лучшая игра

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

· Лучшее приложение для Windows Phone 8 (тут речь идет о приложениях, которые утилизируют новые возможности платформы. Чем лучше и полезней эта утилизация, тем лучше для конкурса)

Ограничения: только жители Украины, приложение должно быть опубликовано с украинского аккаунта (смысл делать метрики другой стране?) и приложение должно быть опубликовано в ходе конкурса (нас интересует что-то новенькое).

До конца конкурса осталось еще 54 дня. Поэтому время еще есть.

Written by Sergiy Baydachnyy

20.12.2012 at 12:54

Опубликовано в Windows Phone

Tagged with ,

Распознавание и обработка речи (часть 1)

2 комментария

 

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

Если Вы еще не пробовали голосовые возможности, то сделайте это, но прежде проверьте настройки Вашего устройства. Для этого перейдите в раздел Settings (Настройки) -> Speech (Голосовые функции) и выберите язык голосовых функций и другие параметры:

image

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

Параметр Play audio confirmation позволяет включить или выключить оповещения о действие, которое сейчас выполняется. Так, после отправки голосовой команды у пользователя есть пару секунд перед тем, как действие будет выполнено. За это время можно успеть отменить операцию, нажав кнопку Cancel.

image

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

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

Последний параметр Enable Speech Recognition Service позволяет включить возможности распознавания голоса в приложениях. Тут ситуация, аналогичная определению местоположения – пользователь может запретить функции распознавания голоса в приложениях. Включая эту функциональность пользователь соглашается с тем, что Microsoft может собирать информацию о запросах пользователя для улучшения словаря и других функций распознавания.

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

image

Тут можно сказать одну из стандартных команд, таких как «Позвонить….», «Запустить…» или вызвать одну из команд, которые декларируют другие приложения. Чтобы увидеть все команды нажмите на вопросительный знак в вернем правом углу окна или произнесите «Что я могу сказать».

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

Голосовые команды

Любое приложение, которое присутствует на телефоне пользователя, может быть запущено с помощью голосовой команды «Запустить <имя приложения>». Но тут есть несколько нюансов. Во-первых, имя приложения может быть плохо читаемым или слишком сложным для произношения. Во-вторых, стандартная команда позволяет запустить приложение без каких-либо параметров. Поэтому, одной из первых задач, актуальной для многих приложений, является необходимость определения команд, которые можно использовать для запуска приложения.

Итак, чтобы определить свои команды для приложения, первым делом нужно задекларировать несколько возможностей, это ID_CAP_NETWORKING, ID_CAP_MICROPHONE, ID_CAP_SPEECH_RECOGNITION:

<Capabilities>
  <Capability Name="ID_CAP_NETWORKING" />
  <Capability Name="ID_CAP_MICROPHONE" />
  <Capability Name="ID_CAP_SPEECH_RECOGNITION" />
</Capabilities>

 

Данные возможности можно задекларировать и в графическом редакторе манифеста. Первая декларация необходима для передачи данных по сети в службу Microsoft для оптимизации службы поиска (не уверен, что тут это используется, но…), вторая – для реализации доступа к микрофону и третья – для активации механизма распознавания голоса.

Включив все необходимые возможности, переходим к описанию команд. Это можно сделать в XML файле. Чтобы не искать структуру этого файла в документации, достаточно воспользоваться готовым шаблонов в Visual Studio. Просто создайте новый элемент на основе шаблона Voice Command Definition:

image

В результате будет создан вот такой файл, который мы рассмотрим сейчас детально:

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
  <CommandSet xml:lang="en-US">
    <CommandPrefix>Contoso Rodeo</CommandPrefix>
    <Example> play a new game </Example>

    <Command Name="PlayGame">
      <Example> play a new game </Example>
      <ListenFor> [and] play [a] new game </ListenFor>
      <ListenFor> [and] start [a] new game </ListenFor>
      <Feedback> Starting a new game... </Feedback>
      <Navigate />
    </Command>

    <Command Name="PlayLevel">
      <Example> replay level two </Example>
      <ListenFor> replay level {number} </ListenFor>
      <Feedback> Going to level {number}... </Feedback>
      <Navigate />
    </Command>

    <Command Name="PlayUnknownLevel">
      <Example> replay level two </Example>
      <ListenFor> [and] replay level {*} </ListenFor>
      <Feedback> Unknown level; going to level selection... </Feedback>
      <Navigate Target="LevelSelect.xaml" />
    </Command>

    <PhraseList Label="number">
      <Item> one </Item>
      <Item> two </Item>
      <Item> three </Item>
    </PhraseList>

  </CommandSet>
</VoiceCommands>

 

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

Следующий важный элемент, это CommandPrefix, который задает команду для запуска приложения. Обычно тут указывается произносимое для пользователя имя приложения, но фактически это аналог команды, которая заменяет стандартную «Запустить <имя приложения>».

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

Наконец, чтобы передать приложению параметры в XML задается набор элементов типа Command. Именно эти элементы расширяют стандартную команду, описанную в CommandPrefix и позволяют пользователю перевести приложение в нужное ему состояние. Например, если Вы пишите приложение для отображения телепрограммы, то его можно запустить по команде «Телепрограмма» или «Телепрограмма на Первом», чтобы открыть список каналов или программу для конкретного канала. Тут три основных элемента, это ListenFor, Feedback и Navigate.

Первый элемент задает часть фразы, которая приводит к выполнению этой команды. Поскольку одну и ту же команду можно произнести по разному, то в одной команде может быть несколько ListenFor элементов, а части фраз, которых может не быть в команде (все зависит от стиля пользователя), размещают в квадратные скобки. Например, можно сказать «Телепрограмма на Первый» или «Телепрограмма сегодня на Первый» или «Телепрограмма Первый» (можно еще и со склонениями играться).

Элемент Feedback задает ту информацию, которая будет отображаться пользователю при выполнении распознанной команды. Наконец, элемент Navigate позволяет задать альтернативную точку входа приложения в отличии от той, которая прописана в манифесте. Обычно этот элемент остается пустым, но возможность есть.

Если вернуться к той же телепрограмме, то каналов, которые поддерживает приложение, может быть достаточно много. Естественно, что в данном случае логично задать какой-то шаблон. Для этого используются фигурные скобки, в которых содержится имя списка с возможным набором слов, подставляемых в шаблон. В файле по умолчанию это шаблон name, описываемый элементом PhraseList, со списком уровней. В случае с телепрограммой, это будет список каналов. В фигурных скобках можно указывать и звездочку {*}, что означает подстановку любой фразы. Обычно {*} используют в команде, которая идет следом за командой, ссылающейся на PhraseList, чтобы обеспечить реакцию приложения в ответ на команду, которая не описывается в предопределенном списке.

Элемент PhraseList еще интересен тем, что его не имеет смысл задавать заранее (если у Вас много фраз), а можно заполнить динамически.

Давайте рассмотрим пример на основе приложения для отображения Телепрограммы. Вот как может выглядеть XML файл:

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
  <CommandSet xml:lang="ru-RU" Name="ru-RU">
    <CommandPrefix>Телепрограмма</CommandPrefix>
    <Example>Телепрограмма</Example>

    <Command Name="ChannelsComm">
      <Example>на Первый</Example>
      <ListenFor> [сегодня] [на] {channels} </ListenFor>
      <Feedback> Телепрограмма на {channels} открывается... </Feedback>
      <Navigate />
    </Command>

    <PhraseList Label="channels">
      <Item>Первый</Item>
    </PhraseList>

  </CommandSet>
</VoiceCommands>

 

Как Вы видите, тут указана поддержка русского языка, а приложению назначается более пристойное имя чем в оригинале (у меня это PhoneApp 9J). Теперь приложение можно запустить, сказав «Запустить Телепрограмма» или «Телепрограмма на Первый» и т. д. Но, прежде чем это начнет работать, необходимо инициализировать этот файл. Это можно сделать при первом запуске приложения с помощью класса VoiceCommandService, который содержит метод InstallCommandSetsFromFileAsync, позволяющем инициализировать команды из XML. Вот как может выглядеть метод, выполняющий инициализацию:

private async void InitVoiceCommands()
{
    if (VoiceCommandService.InstalledCommandSets.Count == 0)
    {
        await VoiceCommandService.InstallCommandSetsFromFileAsync(
           new Uri("ms-appx:///MyVoiceCommand.xml"));
    }
}

 

Тут я также проверил, не были ли инициализированы команды ранее, чтобы не выполнять лишнюю работу во время второго и последующих запусков приложения. Метод можно вызвать из InitializePhoneApplication, который в свою очередь вызывается из конструктора класса App в файле App.xaml.cs.

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

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

await VoiceCommandService.InstalledCommandSets["ru-RU"].UpdatePhraseListAsync(
      "channels", new List<string>() { "Первый", "Новый", "Интер" });

 

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

Наконец, подготовительная работа закончена, осталось дождаться команды пользователя и верно ее обработать. Но тут уже все просто. Достаточно описать метод OnNavigatedTo на тех страницах, куда ведут Ваши команды и использовать класс NavigationService для получения строки запроса (QueryString).

image

Как видно из рисунка выше (я поставил точку останова при отладке в методе OnNavigatedTo), в случае запуска приложения с помощью одной из команд, QueryString содержит несколько параметров. Обязательным является voiceCommandName, который содержит имя команды. Параметр reco содержит текст, который был распознан при отправке команды пользователю на основе заданного шаблона, а параметр channels содержит фразу. Этих данных вполне хватит, чтобы принять решение о дальнейшем поведении приложения.

Written by Sergiy Baydachnyy

18.12.2012 at 17:57

Опубликовано в Windows Phone

Tagged with

Windows Phone 8: Интеграция с экраном блокировки

with one comment

Если Вы уже исследовали настройки телефона на реальном устройстве, то могли обнаружить отдельный раздел Экран блокировки (lock screen). Тут пользователь может задать такие параметры экрана блокировки своего устройства, как:

· изображение, которое будет являться фоном для экрана блокировки;

· приложение, которое будет иметь возможность выводить на экран блокировки короткие сообщения (по умолчанию задан Календарь);

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

Вот как выглядят настройки экрана блокировки по умолчанию:

 

image

 

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

Фон для экрана блокировки

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

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

 

<Extensions>
    <Extension ExtensionName="LockScreen_Background" 
               ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" 
               TaskID="_default" />
</Extensions>

 

Элемент Extensions располагается сразу после элемента Tokens и не содержит никакой изменяемой информации. Иными словами его нужно просто скопировать. Но, если данный элемент отсутствует, то любые попытки установить фон экрана блокировки из кода будут вызывать исключение.

Итак, первый способ сделать Ваше приложение провайдером заставок, это понадеяться на пользователя. Если приложение декларирует расширение LockScreen_Background, то оно появляется в списке приложений, доступных при настройке фона в разделе настроек телефона «Экран блокировки». Тут пользователь может выбрать Ваше приложение и тут же открыть его.

 

image

 

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

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

 

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if ((e.NavigationMode==NavigationMode.New)&&
        (NavigationContext.QueryString.Keys.Contains("WallpaperSettings"))&&
        (NavigationContext.QueryString["WallpaperSettings"].Equals("1")))
    {
        NavigationService.Navigate(new Uri("/Settings.xaml"));
    }

    base.OnNavigatedTo(e);
}

 

Тут нам нужно проверить, что страница действительно была только что создана, что параметр WallpaperSettings был передан и равен единице (если был передан). Если это так, то мы перенаправляем пользователя на страницу настроек, связанных с экраном блокировки, или сразу переходим к установке каких-то изображений.

Второй способ сделать Ваше приложение провайдером изображений на экран блокировки, то явно запросить разрешение на это у пользователя. Для этого следует воспользоваться пространством имен Windows.Phone.System.UserProfile, где есть два нужных нам класса, это LockScreen и LockScreenManager. С помощью LockScreenManager можно определить, является ли наше приложение провайдером изображений в данным момент времени. Для этого используется свойство IsProvidedByCurrentApplication. Если свойство возвращает значение true, то все в порядке и можно приступать к установке изображений. В противном случае, можно запросить право стать провайдером прямо из приложения. Для этого можно воспользоваться методом RequestAccessAsync. Этот метод возвращает значение перечислимого типа LockScreenRequestResult, способное принимать значение Granted или Denied. Это позволяет избежать дополнительных объяснений пользователю и не перебрасывать его на страницу настроек, а решить все на месте. При вызове метода RequestAccessAsync пользователь увидит на экране всплывающее сообщение подобного типа:

 

image

 

Наконец, когда разрешения получены и все проверки сделаны, можно установить новое изображение на экран блокировки. Для этого используется класс LockScreen из того же пространства имен. Тут используется всего два метода, это GetImageUri и SetImageUri, которые позволяют получить имя файла с текущим изображением, а также установить новое изображение. Изображение может находится как внутри пакета, так и в изолированном хранилище. В зависимости от этого при формировании Uri используйте префикс msappx:/// или msappdata:///local/ соответственно. Если изображение находится в сети, то его нужно предварительно скопировать в изолированное хранилище. При этом следите, чтобы имя файла не повторялось при копировании. В противном случае платформа решит, что изображение не поменялось и не произведет обновление.

Сообщения и статус для экрана блокировки

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

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

 

<Extensions>
  <Extension ExtensionName="LockScreen_Notification_IconCount" 
             ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" 
             TaskID="_default" />
  <Extension ExtensionName="LockScreen_Notification_TextField" 
             ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" 
             TaskID="_default" />
</Extensions>

 

Тут, первое расширение декларирует возможность отображать счетчик приложения, а второе – текстовое сообщение. Естественно, что можно вместо двух расширений указать только одно из них. Тут все зависит от функциональности и потребностей приложения.

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

Еще один шаг, который необходимо сделать, это предоставить иконку для отображения статуса. Это специальная иконка приложения, которая отображается рядом со счетчиком и имеет размер 38 на 38 пикселей в формате png. При этом для создания иконки можно использовать только белый цвет, размещенный на прозрачном фоне. Любая другая иконка не пройдет сертификацию. Как только иконка у Вас готова, ее необходимо описать в манифесте, используя элемент DeviceLockImageURI внутри элемента, описывающего основную плитку приложения (например, TemplateFlip):

 

<DeviceLockImageURI IsRelative="true" 
                 IsResource="false">Assets\LockImage.png
</DeviceLockImageURI>

 

Тут указывается месторасположение иконки и путь к ней.

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

Written by Sergiy Baydachnyy

17.12.2012 at 22:12

Опубликовано в Windows Phone

Tagged with

Windows Phone 8 Camp–Видео с события

leave a comment »

Загрузил видео с Windows Phone 8 Camp в сеть. Если кому-то будет не лень, то можете перепубликовать в другие места.

Итак,

1). Обзор новых устройств Nokia, национальный тренер Nokia (217 Mb)

2). Обзор новых возможностей в Windows Phone 8 – часть 1, Сергей Байдачный (372 Mb)

3). Модель работы приложений в Windows Phone 8, Сергей Лутай (393 Mb)

4). Обзор новых возможностей в Windows Phone 8 – часть 2, Сергей Байдачный (457 Mb)

5). Windows Phone 8: лучшие практики для разработки высококлассных приложений, Александр Краковецкий (481 Mb)

6). C++ при разработке Windows Phone 8 приложений, Сергей Байдачный (207 Mb)

 

Презентация с моего доклада тут.

Written by Sergiy Baydachnyy

16.12.2012 at 19:53

Опубликовано в Windows Phone

Tagged with

Использование карт (часть 2)

leave a comment »

Расширение возможностей по работе с картами

Очень много задач не только требуют отобразить карту и какие-то элементы на ней, но и реализовать поиск координат места по названию и наоборот, отобразить путь, посчитать расстояние до цели и др. Карты от Nokia позволяют реализовывать и все эти задачи. Для этого обратимся к пространству имен Microsoft.Phone.Maps.Services, где содержатся все утилитные классы.

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

Чтобы найти позицию по адресу, необходимо использовать класс GeocodeQuery. Этот класс содержит два свойства, которые позволяют установить начальные параметры для поиска, это SearchTerm и GeoCoordinate. Первое свойство SearchTerm позволяет задать параметры поиска. Тут не обязательно должен быть точный адрес, так как класс GeocodeQuery позволяет получить коллекцию результатов поиска. Поскольку результатов может быть несколько, то для правильной сортировки и оптимизации поиска задается свойство GeoCoodinate. Обычно это свойство задает текущую позицию устройства и позволяет не выдавать заведомо бесполезные результаты (например, адреса в США, когда Вы находитесь в Украине). При сложных сценариях GeoCoordinate можно устанавливать в любую позицию.

Создав объект типа GeocodeQuery и задав основные свойства, можно переходить к поиску. Для этого используется метод QueryAsync, который после своего завершения работы инициирует событие QueryCompleted. Поэтому, следует повесить обработчик события QueryCompleted и можно вызывать метод. Внутри обработчика события QueryCompleted следует проверить количество результатов и возможность возникновения ошибки. Вот небольшой пример кода, который выполняет поиск адреса:

 

 private async void OneShotLocation_Click()
 {
     GeocodeQuery MyGeocodeQuery = new GeocodeQuery();
     MyGeocodeQuery.SearchTerm = "Dnepropetrovsk, Marksa 101";
     MyGeocodeQuery.GeoCoordinate = new GeoCoordinate(50.4556, 30.5143);

     MyGeocodeQuery.QueryCompleted += MyGeocodeQuery_QueryCompleted;
     MyGeocodeQuery.QueryAsync();
 }

 void MyGeocodeQuery_QueryCompleted(object sender, 
          QueryCompletedEventArgs<IList<MapLocation>> e)
{
    if ((e.Error == null)&&(e.Result.Count>0))
    {
        Ellipse myCircle = new Ellipse();
        myCircle.Fill = new SolidColorBrush(Colors.Blue);
        myCircle.Height = 20;
        myCircle.Width = 20;
        myCircle.Opacity = 50;

        MapOverlay myLocationOverlay = new MapOverlay();
        myLocationOverlay.Content = myCircle;
        myLocationOverlay.PositionOrigin = new Point(0.5, 0.5);
        myLocationOverlay.GeoCoordinate =
            e.Result.First().GeoCoordinate;

        MapLayer myLocationLayer = new MapLayer();
        myLocationLayer.Add(myLocationOverlay);

        myMap.Layers.Add(myLocationLayer);
        myMap.SetView(e.Result.First().GeoCoordinate, 14);
    }
}

 

Обратите внимание на то, что QueryAsync не поддерживает возможность использовать подход await/async, а требует создания обработчика.

Используя коллекцию результатов можно предоставить пользователю выбор, а если более детально посмотреть на содержимое e.Result, то можно получить не только координаты, но и район города. Тут используются объекты классов MapLocation и MapAddress.

Если у Вас обратная задача и по GPS координатам необходимо найти адрес, то это также можно легко сделать с помощью класса ReverseGeocodeQuery:

 

ReverseGeocodeQuery MyGeocodeQuery = new ReverseGeocodeQuery();
MyGeocodeQuery.GeoCoordinate = new GeoCoordinate(50.4556, 30.5143);

MyGeocodeQuery.QueryCompleted += MyGeocodeQuery_QueryCompleted;
MyGeocodeQuery.QueryAsync();

 

Тут только один параметр для поиска – координаты места, адрес которого необходимо найти.

Остается еще одна задача, которую можно выполнить с помощью карт Nokia, это прокладка пути. При этом путь можно проложить как для пешехода, так и для автомобиля, получив все данные, которые дают современные GPS системы. Тут используется класс RouteQuery, который содержит три основных свойства:

· RouteOptimization – позволяет задать параметр, определяющий оптимальный для пользователя путь: экономный по расстоянию или по времени;

· TravelMode – позволяет указать, как будет передвигаться пользователь: пешком или на автомобиле;

· Waypoints – содержит коллекцию объектов типа GeoCoordinate, перечисляющую те места, где планирует побывать пользователь (в самом простом случае, это начало и конец пути).

Вот пример кода, который задает основные параметры для объекта класса RouteQuery:

 

private async void OneShotLocation_Click()
{
    RouteQuery query = new RouteQuery();
    query.RouteOptimization = RouteOptimization.MinimizeDistance;
    query.TravelMode = TravelMode.Driving;
    query.Waypoints = new List<GeoCoordinate>()
        {
            new GeoCoordinate(50.46,30.5303),
            new GeoCoordinate(50.4556,30.5143)
        };

    query.QueryCompleted += query_QueryCompleted;
    query.QueryAsync();
}

 

Обработчик события QueryCompleted содержит объект типа Route, который можно тут же добавить на карту:

 

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
    MapRoute MyMapRoute = new MapRoute(e.Result);
    myMap.AddRoute(MyMapRoute);
    myMap.SetView(e.Result.BoundingBox);
}

 

Исследуя объект типа Route, можно получить всю необходимую информацию. Например, коллекция Geometry содержит набор объектов типа GeoCoordinate, из которых строится путь, а свойство Legs позволяет получить время пути, расстояние и список мест, где необходимо выполнить изменение пути (инструкций). Все это можно использовать для построения системы любой сложности.

Расширение возможностей с помощью Windows Phone toolkit

Если Вы используете в своем приложении Windows Phone Toolkit, который можно найти по адресу http://phone.codeplex.com/, то можете немного расширить возможности существующего API для карт дополнительными методами и классами.

Как Вы заметили, все асинхронные методы стандартного API требуют создания обработчика события. Windows Phone Toolkit расширяет эти существующие методы аналогами, работающими через async/await. Так, метод GetRouteAsync расширяет класс RouteQuery, а методом GetMapLocationsAsync расширяются классы GeocodeQuery и ReverseGeocodeQuery. Так, предыдущий пример будет выглядеть следующим образом:

 

private async void OneShotLocation_Click()
{
    RouteQuery query = new RouteQuery();
    query.RouteOptimization = RouteOptimization.MinimizeDistance;
    query.TravelMode = TravelMode.Driving;
    query.Waypoints = new List<GeoCoordinate>()
        {
            new GeoCoordinate(50.46,30.5303),
            new GeoCoordinate(50.4556,30.5143)
        };

    query.QueryCompleted += query_QueryCompleted;
    Route r=await query.GetRouteAsync();

    MapRoute MyMapRoute = new MapRoute(r);
    myMap.AddRoute(MyMapRoute);
    myMap.SetView(r.BoundingBox);
}

 

Кроме асинхронных методов, Windows Phone Toolkit расширяет возможности класса Map. Во-первых, тут появляется класс Pushpin, который был в Bing Maps SDK и представляет собой готовую метку для карт. Данные метки можно установить с помощью расширения Children, задающего коллекцию элементов на карте. Во-вторых, тут появился MapItemsControl, который также добавляется в коллекцию Children, но при этом сам является источником для коллекции данных, позволяя реализовать механизм связывания карты и данных.

Ниже пример инициализации коллекции Children и добавления в нее элемента PushPin:

 

ObservableCollection<DependencyObject> children = 
MapExtensions.GetChildren(myMap);

children.Add(new Pushpin() 
{ 
GeoCoordinate = new GeoCoordinate(50.46, 30.5303) 
});

 

А вот этот пример показывает, как можно определить связывание карты и данных в XAML:

 

<maps:Map x:Name="Map" Grid.Row="1" Hold="OnMapHold">
    <maptk:MapExtensions.Children>
        <maptk:Pushpin x:Name="RouteDirectionsPushPin"           
                       Visibility="Collapsed"/>
        <maptk:MapItemsControl Name="StoresMapItemsControl">
            <maptk:MapItemsControl.ItemTemplate>
                <DataTemplate>
                    <maptk:Pushpin GeoCoordinate="{Binding GeoCoordinate}" 
                                   Visibility="{Binding Visibility}" 
                                   Content="{Binding Address}"/>
                </DataTemplate>
            </maptk:MapItemsControl.ItemTemplate>
        </maptk:MapItemsControl>
        <maptk:UserLocationMarker x:Name="UserLocationMarker" 
                                  Visibility="Collapsed"/>
    </maptk:MapExtensions.Children>
</maps:Map>

 

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

Как видите, если у Вас используется связывание с данными, то Toolkit очень полезен. Более того, его можно использовать для миграции Ваших приложений с WP 7 на WP 8.

Written by Sergiy Baydachnyy

16.12.2012 at 11:18

Опубликовано в Windows Phone

Tagged with