[Windows Phone 8.1] Integrando nuestra aplicación con Cortana

CortanaIntroducción

En el pasado //BUILD/ de Microsoft, Joe Belfiore vicepresidente del programa Windows Phone presento Cortana. Cortana es el asistente virtual de Windows Phone y entre la enorme cantidad de funcionalidades incluidas además de las que van incorporando poco a poco (como por ejemplo, recomendaciones basadas en búsquedas de Foursquare) cabe destacar que es una plataforma sobre la que se pueden desarrollar aplicaciones de terceros. En este artículo vamos a centrarnos en analizar paso a paso como integrar una aplicación Windows Phone con Cortana.

¿Te apuntas?

Nuestra Aplicación

Para integrar una aplicación Windows Phone con Cortana lo primero que necesitamos es… una aplicación!. Vamos a crear una aplicación sencilla pero que nos sea válida para lograr nuestros objetivos. Nuestra aplicación permitirá consultar cualquier clasificación de pilotos de Formula 1 celebrada hasta la actual:

Nuevo proyecto

Nuevo proyecto

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo. Como hemos comentado, nuestra aplicación permitirá consultar cualquier clasificación de pilotos de Formula de cualquier año, necesitamos un selector de año. Añadimos un botón en nuestra interfaz:

<Button />

Al botón le añadiremos un ListPickerFlyout, nos permitirá mostrar un listado de opciones al pulsar sobre el botón:

<Button>
     <Button.Flyout>
          <ListPickerFlyout
               Title="SELECCIONA AÑO">
               <ListPickerFlyout.ItemTemplate>
                    <DataTemplate>
                         <StackPanel>
                              <TextBlock Text="{Binding}" FontSize="{StaticResource TextStyleExtraLargeFontSize}"/>
                         </StackPanel>
                    </DataTemplate>
               </ListPickerFlyout.ItemTemplate>
          </ListPickerFlyout>
     </Button.Flyout>
</Button>

La interfaz de la página principal de nuestra aplicación es la siguiente:

Nuestra página principal

Nuestra página principal

El control ListPickerFlyout muestra una colección indicada mediante la propiedad ItemsSource, y el elemento seleccionado lo podemos obtener mediante la propiedad SelectedItem:

<Button
     HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
     Content="{Binding SelectedYear}">
     <Button.Flyout>
          <ListPickerFlyout
               Title="SELECCIONA AÑO" ItemsSource="{Binding Years}"
               SelectedItem="{Binding SelectedYear, Mode=TwoWay}">
               <ListPickerFlyout.ItemTemplate>
                    <DataTemplate>
                         <StackPanel>
                              <TextBlock Text="{Binding}" FontSize="{StaticResource TextStyleExtraLargeFontSize}"/>
                         </StackPanel>
                    </DataTemplate>
               </ListPickerFlyout.ItemTemplate>
          </ListPickerFlyout>
     </Button.Flyout>
</Button>

Al pulsar el botón:

ListPickerFlyout para la selección de año

ListPickerFlyout para la selección de año

¿Pero la propiedad Years bindeada a la propiedad ItemsSource de donde obtiene la información?. En nuestra viewmodel:

private ObservableCollection<string> _years;
private string _selectedYear;

public ObservableCollection<string> Years
{
     get { return _years; }
     set { _years = value; }
}

private void LoadYears()
{
     for (var i = 1950; i <= DateTime.Now.Year; i++)
     {
          Years.Add(i.ToString());
     }
}

public string SelectedYear
{
     get { return _selectedYear; }
     set
     {
          _selectedYear = value;
          RaisePropertyChanged();
     }
}

Creamos una colección de años que usaremos para bindear al control ListPickerFlyout. La información, los años seleccionables, los rellenamos con el sencillo método LoadYears que podéis ver en las líneas superiores. Una vez seleccionado un año del que obtener la clasificación de pilotos, necesitamos lanzar la búsqueda:

<Page.BottomAppBar>
     <CommandBar>
          <AppBarButton Label="buscar" Icon="Find" Command="{Binding SearchCommand}" />
     </CommandBar>
</Page.BottomAppBar>

Añadimos una CommandBar con un botón que nos permita realizar la búsqueda. En la viewmodel:

private ICommand _searchCommand;

public ICommand SearchCommand
{
     get { return _searchCommand = _searchCommand ?? new DelegateCommand(SearchCommandDelegate); }
}

public void SearchCommandDelegate()
{
     AppFrame.Navigate(typeof(SearchView), _selectedYear);
}

Añadimos un comando de modo que al ejecutarse, navega a una nueva vista pasando como parámetro el año seleccionado. En la vista a la que navegamos tendremos un listado:

<GridView    
     ItemsSource="{Binding DriverStanding}"
     ItemTemplate="{StaticResource DriverTemplate}"
     ItemsPanel="{StaticResource ItemPanelTemplate}"
     SelectionMode="None"
     IsItemClickEnabled="True">

Con la plantilla que definirá el aspecto de cada elemento de la lista:

<DataTemplate x:Key="DriverTemplate">
     <Grid Margin="5" Width="300">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid>
                <Ellipse Height="50" Width="50" Fill="Red"/>
                <TextBlock Text="{Binding Position}" FontSize="28" HorizontalAlignment="Center"
                           VerticalAlignment="Center" Foreground="White" />
            </Grid>
            <StackPanel Grid.Column="1" Margin="15, 0">
                <TextBlock Text="{Binding Driver.CompleteName}" FontSize="24" />
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Points}" Foreground="Red" />
                    <TextBlock Text=" Points" />
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Wins}" Foreground="Red" />
                    <TextBlock Text=" Wins" />
                </StackPanel>
            </StackPanel>
     </Grid>
</DataTemplate>
    
<ItemsPanelTemplate x:Key="ItemPanelTemplate">
     <StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>

Y por su puesto, su correspondiente viewmodel donde sobrescribimos el evento OnNavigatedTo que se lanzará cada vez que entremos a la página para obtener el parámetro de navegación (el año del que deseamos obtener la clasificación) y lanzamos la carga de la información:

public override async System.Threading.Tasks.Task OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
{
     var season = args.Parameter.ToString();
     await LoadStandingsData(season);
}

La carga de la información accederá a un servicio que nos permite obtener las clasificaciones de la Formula 1 por año y volcará la información en una colección disponible como propiedad públicada bindeada a la lista:

private async Task LoadStandingsData(string season)
{
     var driverStandings = await _standingService.GetSeasonDriverStandingsCollectionAsync(season);
     var drivers = driverStandings.StandingsLists.First().DriverStandings;

     foreach (var driver in drivers)
     {
          DriverStanding.Add(driver);
     }
}

El resultado:

Resultado de la búsqueda

Resultado de la búsqueda

En la captura superior podemos ver el resultado de la búsqueda de la clasificación de pilotos del año 2006. Un ejemplo sencillo pero interesante que podría ganar mucho realizando la integración con Cortana.

Cortana

Inspirado por un famoso personaje de Halo que era un asistente digital personal para el protagonista del juego, Master Chief, nos llega un asistente de voz personal a Windows Phone.

NOTA: Tras la voz de Cortana esta Jen Taylor la misma mujer que dio voz al personaje en el videojuego.

En otras plataformas contaban con Siri (Apple) o Google Now (Google), asistentes de voz más versátiles que el sistema de reconocimiento de voz con el que contábamos hasta ahora en Windows Phone.

Cortana

Cortana

Cortana llega para ir más alla que todo lo existente hasta ahora.

¿Cómo?

Cortana no se limitará a un sistema que reaccionará ante comandos de voz, si no que tomará múltiples fuentes para contar con la mayor cantidad de información posible para interaccionar de la forma más precisa posible.

La primera vez que usemos Cortana nos realizará algunas preguntas básicas sobre nosotros. A partir de ese momento, Cortana mirara en contactos, lugares habituales, intereses… todo lo necesario para aprender lo máximo posible de nosotros mismos.

NOTA: El nivel de acceso de Cortana a nuestros datos es configurable.

Asi que Cortana no se limita a responder órdenes o realizar búsquedas básicas como lugares, el tiempo o resultados deportivos, además puede establecer recordatorios como recordarle a tu madre que tal le va el nuevo Windows Phone que le has regalado por ejemplo.

Por si fuese poco, otra gran diferencia de Cortana con otros asistentes digitales es la posibilidad de interactuar con aplicaciones de terceros.

Integración con Cortana

Vamos a integrar nuestra aplicación con Cortana.

¿Qué quiere decir esto?

Sencillo, desde Cortana permitiremos preguntar por “Formula One standings for 2006” y que en lugar de realizar una búsqueda en Bing, abra nuestra aplicación, realice la búsqueda correspondiente y muestre la información completa de la clasificación de pilotos de Formula 1 del año 2006. De esta forma, por un lado ofrecemos una experiencia cada vez más completa e integrada al usuario desde Cortana y además logramos otro punto de entrada y un mayor uso de nuestra aplicación.

Para integrar nuestra aplicación con Cortana realizaremos tres sencillos pasos:

1º Creando la definición de comandos de voz (VCD)

El usuario puede mediante voz tratar con Cortana para activar la aplicación y ejecutar una acción concreta. Por ejemplo, el usuario que usa la aplicación Formula 1 Standings podría mantener presionado el botón Buscar para iniciar Cortana y decir: “Formula 1 Standings for 2006”. Esto activará la aplicación Formula 1 Standings, que, seguidamente, navegará a la página de búsqueda realizando la acción. El primer paso para conseguir esto sera crear un archivo VCD (Definición de comando de voz). El archivo VCD es un documento XML en el que definimos todos los comandos de voz que el usuario puede utilizar para lanzar acciones en la aplicación.

Creamos el archivo VCD. En Visual Studio, hacemos clic derecho en el proyecto y seleccionamos Agregar->Nuevo elemento y luego Archivo de texto. Tras agregar el archivo de texto debemos asignarle el nombre que consideremos oportuno, en nuestro ejemplo se le ha llamado VoiceCommandDefinitions y lo más importante debemos cambiar la exntesión a .xml. Además, En la ventana de Propiedades del archivo xml establecemos la propiedad Acción de compilación a Contenido y a continuación Copiar en el directorio de salida en Copiar si es posterior. Nuestra definición de comando de voz es la siguiente:

<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.1">
  <CommandSet xml:lang="en-us">
    <CommandPrefix>formula one standing for</CommandPrefix>
    <Example>Search Formula One Standings</Example>
    <Command Name="for">
      <Example>2005</Example>
      <ListenFor>[for] {dictatedSearchTerms}</ListenFor>
      <Feedback>checking that out on Ergast API for you, give me a second</Feedback>
      <Navigate Target="SearchView.xaml"/>
    </Command>
    <PhraseTopic Label="dictatedSearchTerms" Scenario="Natural Language">
   </PhraseTopic>
</CommandSet>
</VoiceCommands>

Todos los comandos de voz tienen:

  1. Una frase de ejemplo que muestra como lanzar el comando.
  2. Las palabras que se reconocerán para lanzar el comando.
  3. El texto que Cortana mostrará al reconocer el comando.
  4. La pantalla a la que navegará la aplicación al reconocer el comando.

Analicemos nuestra definición de comandos de voz paso a paso:

  • <CommandPrefix></CommandPrefix> Puede ser el nombre de la aplicación o la frase que el usuario utilizará para lanzar nuestra aplicación.
  • <Example></Example> Ejemplo de más alto nivel indicando al usuario un ejemplo delo que puede hacer.
  • <Command></Command> Unidad lógica de lo que el usuario quiere hacer. Contiene lo que el usuario dice, lo que Cortana responde y lo que Cortana hace.
  • <ListenFor></ListenFor> Una o más frases.
  • <Feedback></Feedback> Feedback visual trasmitido por Cortana hacia al usuario tras reconocer un comando.
  • <Navigate></Navigate> Acción a realizar tras reconocer el comando. Es opcional en apps no-Silverlight.

2º Registrar el XML del VCD en el inicio de la App

Durante la ejecución de la aplicación, registraremos el conjunto de comandos contenidos en el archivo VCD:

async Task InstallVoice()
{
     var storageFile =
          await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///VoiceCommandDefinitions.xml"));

     await VoiceCommandManager.InstallCommandSetsFromStorageFileAsync(storageFile);
}

Utilizamos el método GetFileFromApplicationUriAsync que nos permitirá acceder a recursos de la aplicación utilizando URIs “ms-appx://” o “ms-appdata://”. En este caso lo usamos para acceder al archivo xml del VCD. A continuación, utilizamos la clase estática VoiceCommandManager que nos permite acceder o instalar conjunto de definiciones de comandos de voz mediante archivo VCD. En este caso, procedemos a la instalación utilizando el método InstallCommandSetsFromStorageFileAsync.

NOTA: Para que la instalación de voces se pueda realizar debemos añadir el micrófono como una capacidad de la aplicación.

Añadir el Micrófono en las capacidades de la App

Añadir el Micrófono en las capacidades de la App

3º Gestionar la activación de comandos de voz

Cuando el usuario utilizando Cortana ejecuta una de nuestros comandos de voz en nuestra aplicación se lanza el evento OnActivated. Realmente este evento forma una parte importante del ciclo de vida de las aplicaciones Windows Phone. Sin embargo, en este caso, al lanzarse el evento por motivo de un comando de voz recibimos un parámetro de tipo VoiceCommand.

Gestión de la App desde comando de voz

Gestión de la App desde comando de voz

Tras una pequeña validación, navegaremos a la página principal pasando como parámetro la información recibida desde Cortana con el objeto de tipo SpeechRecognitionResult:

protected override void OnActivated(IActivatedEventArgs args)
{
     if (args.Kind == ActivationKind.VoiceCommand)
     {
          var voiceArgs = (IVoiceCommandActivatedEventArgs)args;
          var result = voiceArgs.Result;

          var frame = Window.Current.Content as Frame;
          frame.Navigate(typeof(SearchView), result);
          Window.Current.Content = frame;

          Window.Current.Activate();
      }

      base.OnActivated(args);
}

Con la información recibida desde Cortana, gestionaremos el contenido en una clase (la viewmodel de la página principal) y redigiremos al usuario a la vista correspondiente con la información esperada:

Gestión del comando de voz

Gestión del comando de voz

En la viewmodel de la vista a la que navegamos, en el método sobrescrito OnNavigatedTo, capturaremos el parámetro con la información enviada por Cortana, obtendremos el valor de la propiedad Text que indica el valor indicado por el usuario, en nuestro ejemplo el año del que desea ver la clasificación de pilotos y se lo pasamos a un método encargado de gestionar el comando de voz:

public override async System.Threading.Tasks.Task OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs args)
{
     var season = string.Empty;

     if (args.NavigationMode == NavigationMode.New)
     {
          var voiceResult = args.Parameter as SpeechRecognitionResult;

          if (voiceResult != null)
               season = voiceResult.Text;
          else
               season = args.Parameter.ToString();
     }

     if (!string.IsNullOrEmpty(season))
          await LoadStandingsData(season);
}

Hasta aqui todo preparado. Puedes descargar el ejemplo completo a continuación:

¿Probamos?

Comenzamos abriendo Cortana:

Entramos en Cortana

Entramos en Cortana

Podemos consultar que aplicaciones estan integradas y que podemos decir preguntando “What can i say?”:

Apps integradas con Cortana

Apps integradas con Cortana

Vemos que nuestra aplicación de ejemplo que nos permite realizar consultas sobre las clasificaciones de la formula 1 esta disponible. Si pulsamos sobre ella:

¿Que podemos decir?

¿Que podemos decir?

Vemos un ejemplo de lo que podemos consultar. Realizamos una consulta:

Ejecutando un comando de voz

Ejecutando un comando de voz

Cortana interpreta el comando, detecta nuestra aplicación, muestra un mensaje de feedback al usuario de lo que se esta realizando y lanza nuestra aplicación:

Ejecutando la acción!

Ejecutando la acción!

El resultado final:

Resultado final!

Resultado final!

Como hemos comentado logramos otro punto de entrada hacia nuestra aplicación asi como ofrecer una experiencia mucho más completa e integrada desde Cortana. Comparemos el resultado a la búsqueda sin existir nuestra aplicación:

Resultado sin nuestra App

Resultado sin nuestra App

En video:

Consideraciones en el diseño

En el proceso de integración de nuestra aplicación con Cortana debemos tener en cuenta una serie de consideraciones:

  • Usa comandos sencillos en los elementos <ListenFor> del archivo VCD.
  • Analiza con calma los comandos de voz. Se cuidadoso con el coste y beneficio de cada palabra.
  • Adapta la interfaz y la respuesta otorgada segun la petición del usuario.
  • Manten al usuario siempre informado de todo lo que esta ocurriendo.

Más información

Un pensamiento en “[Windows Phone 8.1] Integrando nuestra aplicación con Cortana

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s