[Windows Phone 8.1] Crear menu lateral deslizante (similar al de la App Facebook)

Introducción

El SDK de Windows Phone es bastante versátil y nos proporciona una
fuente bastante extensa de controles como para realizar una interfaz de
usuario rica y atractiva. De hecho, tenemos ciertos controles como el
Hub o el Pivot encargados de crear la estructura principal de nuestra
aplicación determinando el acceso a los distintos apartados. Sin
embargo, no es una forma única y exclusiva. Son múltiples los casos de
aplicaciones en la tienda con ciertos menus deslizantes o determinos
apartados personalizados. Un ejemplo claro de ello es la aplicación de Facebook que cuenta con un menu deslizante como el siguiente:

¿Cómo realizamos dicho menu?

Bien, tenemos múltiples formas de lograrlo. Con el uso combinado de
paneles, animaciones y gestos podríamos conseguir reproducir el menu de
Facebook. Sin embargo, tenemos una librería llamada SlideView disponible
tanto para Windows Phone 8.0 como para Windows Phone 8.1 que tiene como
objetivo permitirnos crear un menu de este tipo con suma facilidad.

En este artículo vamos a aprender como crear menus deslizantes al estilo Facebook utilizando la librería SlideView.

¿Te apuntas?

Crear el menu en una vista

Comenzamos creando un nuevo proyecto:

Para crear el menu lateral deslizante tipo Facebook, como mencionamos
previamente vamos a utilizar una librería gratuita creada por Cyril Cathala, MVP de C# llamada SlideView.
La librería contiene múltiples controles inspirados por el menu
deslizante de Facebook que analizaremos poco a poco en este artículo.

Comenzamos añadiendo la librería.

La librería la tenemos disponible en NuGet por lo que podemos instalarlo usando Nuget Package Manager. En las referencias del proyecto hacemos clic derecho y seleccionamos la opción Manage NuGet Packages …

En la ventana modal que nos aparece, en la parte superior derecha donde podemos realizar una búsqueda, buscamos por “slideview”:

Seleccionamos el primero de los elementos y pulsamos el botón Install.
Tras un breve periodo donde se procede a descargar e incluir las
librerías en las referencias del proyecto, tendremos el siguiente
resultado:

Nos centramos en nuestra página principal, MainPage.xaml. Vamos a utilizar el control SlideView de la librería:

<Grid>
     <ctl:SlideView SelectedIndex="1">
          <Grid Background="Red"
                Width="350">
                <TextBlock Text="Left"
                           FontSize="48"
                           HorizontalAlignment="Center"/>
          </Grid>
          <Grid Background="LightGray">
                <TextBlock Text="Main Content"
                           FontSize="48"
                           HorizontalAlignment="Center"/>
          </Grid>
          <Grid Background="Firebrick"
                  Width="350">
                <TextBlock Text="Right"
                           FontSize="48"
                           HorizontalAlignment="Center"/>
          </Grid>
     </ctl:SlideView>
</Grid>

Es un control muy sencillo. En su interior iremos añadiendo paneles
que se apilaran uno tras otro de forma horizontal. Podemos establecer un
ancho fijo o dejar el ancho automático. En caso de dejar un ancho
automático, el panel se ajustará al ancho de la pantalla. Además,
podemos indicar el panel visible de forma muy simple mediante la
propiedad SelectedIndex del control.

El resultado es el siguiente:

Podéis descargar el ejemplo realizado a continuación:

Crear el menu a nivel de aplicación

El ejemplo anterior es muy simple y nos permite crear menus laterales
con suma facilidad. Sin embargo, los paneles laterales solo aparecerán
en la vista donde utilicemos el control.

¿Qué ocurre si deseamos los menus laterales a lo largo de toda la aplicación?

Para estos casos tenemos disponible en la librería otro control, variante del Frame, llamado SlideApplicationFrame. Para aprender a utilizar este frame vamos a crear otro proyecto diferente al anterior. 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.

Para utilizar el control, necesitamos sustituir el Frame de la aplicación por el frame de tipo SlideApplicationFrame. Una forma sencilla de definir el nuevo frame es directamente en XAML en App.xaml:

<ctl:SlideApplicationFrame Header="Slide View"
                           Background="Green"
                           x:Key="RootFrame">
     <ctl:SlideApplicationFrame.LeftContent>
          <shell:LeftView />
     </ctl:SlideApplicationFrame.LeftContent>
     <ctl:SlideApplicationFrame.RightContent>
          <shell:RightView />
     </ctl:SlideApplicationFrame.RightContent>
</ctl:SlideApplicationFrame>

Definimos el contenido del menu lateral izquierdo mediante la propiedad LeftContent y el derecho mediante RightContent.
En cada panel podemos incluir una vista con su contenido y gestionada
por su propio viewmodel. El contenido del menu lateral izquierdo:

<Grid x:Name="LayoutRoot"
      Background="LightBlue">
     <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="*" />
     </Grid.RowDefinitions>
     <Grid Background="Blue">
          <TextBlock Margin="6,12"
                     HorizontalAlignment="Center"
                     Text="Left View" />
     </Grid>
     <Grid Grid.Row="1">
          <TextBlock Text="Left side!"/>
     </Grid>
</Grid>

El resultado:

El menu lateral derecho es otra vista con el siguiente contenido:

<Grid x:Name="LayoutRoot"
      Background="Orange">
      <Grid.RowDefinitions>
           <RowDefinition Height="Auto" />
           <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <Grid Background="OrangeRed">
           <TextBlock Margin="6,12"
                      HorizontalAlignment="Center"
                      Text="Right View" />
      </Grid>
      <Grid Grid.Row="1">
           <TextBlock Text="Right side!"/>
      </Grid>
</Grid>

El resultado:

NOTA: Podemos personalizar la cabecera del control, los botones para expandir cada menu e incluso el panel principal (LeftPanelButton, MainPanel, etc).

Una vez definido el frame SlideApplicationFrame bastará con acceder a el y reemplazar el frame de la aplicación, rootFrame:

rootFrame = this.Resources["RootFrame"] as SlideApplicationFrame;

Sencillo, ¿verdad?

Un detalle más…

Aunque no esta directamente relacionado con los menus laterales, la librería cuenta con otro control más llamado AutoHideBar.
El control AutoHideBar se muestra cuando realizamos scroll hacia arriba
en la parte superior de un listado y se oculta al hacer scroll hacia
abajo. Vamos a utilizar el control. Su definición es sencilla:

<ctl:AutoHideBar />

El objetivo del control es mostrar información o botones de acción al
hacer scroll hacia la parte superior de una lista. Por ese motivo, para
poder usar el control necesitaremos, un control listado. En la viewmodel de la vista vamos a definir primero una colección:

private ObservableCollection<string> _list;
 
public ObservableCollection<string> List
{
     get { return _list; }
     set { _list = value; }
}

Creamos un método que rellenará la propiedad List con datos:

private void FillData()
{
     for(int i = 0; i < 50; i++)
     {
          _list.Add(string.Format("List Item Number {0}", i + 1));
     }
}

Por último, en la vista, definiremos un control ListView con la propiedad ItemsSource bindeada a la propiedad List:

<ListView x:Name="List"
           ItemsSource="{Binding List}">
     <ListView.Header>
          <Grid Height="80"/>
     </ListView.Header>
     <ListView.ItemTemplate>
          <DataTemplate>
               <Grid>
                    <TextBlock Text="{Binding}"
                               FontSize="18" />
               </Grid>
          </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

Utilizamos la propiedad Header del ListView para establecer un encabezado y el control AutoHideBar:

<ctl:AutoHideBar ScrollControl="{Binding ElementName=List}"
                 Background="DarkGreen">
     <Grid>
          <TextBlock Text="AutoHideBar"
                     HorizontalAlignment="Center" />
     </Grid>
</ctl:AutoHideBar>

Para que el control AutoHideBar funcione correctamente, necesitamos bindear el control lista en la propiedad ScrollControl del control AutoHideBar.

Podéis ver al Frame SlideApplicationFrame y al controlAutoHideBar en acción en el siguiente video:

Podéis descargar el ejemplo realizado a continuación:

Más información

[Windows Phone 8.1] Application Data APIs

Introducción

Entre la gran cantidad de novedades recibidad en el SDK de Windows Phone 8.1 brillan con fuerza las APIs de Application Data.
Nuevas APIs que brindan mas opciones y que en su mayoría se alinean
mucho con las APIs ya existentes en Windows Runtime (WinRT). Algo que
permite a los desarrolladores Windows Store dar el salto con facilidad a
Windows Phone además de permitir compartir código con facilidad.

En esta entrada vamos a analizar las novedades en las APIs de Application Data.

¿Te apuntas?

Application Data APIs

Tanto en aplicaciones Silverlight 8.1 como en aplicaciones Windows XAML tenemos disponible las siguientes carpetas:

  • LocalFolder:
    Un viejo conocido. Ya lo teníamos disponible en Windows Phone 8 e
    incluso era la carpeta usada como Isolated Storage desde Windows Phone
    7. Guardaremos información que persiste entre actualizaciones de la
    aplicación y entra dentro de los datos guardados al realizar un backup
    del sistema.
var localFolder = ApplicationData.Current.LocalFolder;
var file = await roamingFolder.CreateFileAsync("file.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, "Ejemplo archivo en LocalFolder");
  • RoamingFolder:
    Almacenamiento muy útil. Al guardar datos en RoamingData, la
    información estará disponible en todos los dispositivos donde la
    aplicación este instalada (con el mismo id). Ideal para guardar la
    configuración de la aplicación y mantenerla sincronizada entre la
    aplicación Windows Phone y la Windows Store por ejemplo.
var roamingFolder = ApplicationData.Current.RoamingFolder;
var file = await roamingFolder.CreateFileAsync("file.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, "Ejemplo archivo en RoamingFolder");
Podemos controlar cuando los datos de roaming han sido cambiados en alguna de las aplicaciones gracias al evento DataChanged.
Windows.Storage.ApplicationData.Current.DataChanged +=
      new TypedEventHandler<ApplicationData, object>(DataChangeHandler);
  
private async void DataChangedHandler(ApplicationData appData, object o)
{
  
}
  • TemporaryFolder:
    Aqui guardaremos información sin tener la necesidad de borrarla más
    tarde. La información se guardará entre las distintas sesiones pero
    cuando el sistema requiera espacio (Ejemplo: poca memoria disponible),
    eliminará la información.  Es un lugar idóneo donde guardar datos
    obtenidos de peticiones web, servicios o imágenes por ejemplo.
var tempFolder = ApplicationData.Current.TemporaryFolder;
var tempFile = await roamingFolder.CreateFileAsync("file.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(tempFile, "Ejemplo archivo en TemporaryFolder");
Podemos acceder a ficheros en la carpeta temporal desde XAML utilizando el protocolo “ms-appdata:///temp/”.

NOTA: La información de TemporaryFolder no se guarda en los Backups realizados al sistema.

<img src="ms-appdata:///temp/file.png" alt="" />

Otra de las novedades, esta vez solo disponible en las APIs de Windows Phone 8.1 es LocalCacheFolder. Almacenamiento muy similar a LocalFoler pero con ligeras diferencias. Los datos no se copian nunca al realizar backups.

NOTA: Podemos usar la API solo en Windows Phone,
en un proyecto Shared necesitamos definir directivas de compilación
para evitar errores.

#if WINDOWS_PHONE_APP
    var localCache = ApplicationData.Current.LocalCacheFolder;
#endif

Más información

[Windows Phone 8.1] Probando notificaciones en el emulador

Introducción

Uno de los primeros cambios visibles en el nuevo SDK de Windows Phone 8.1 es el emulador.
Contamos con una gran variedad de emuladores con diferentes
resoluciones, tamaños de pantalla y memoria lo que nos permite probar
una gran cantidad de la funcionalidad de nuestras aplicaciones. Entre la
gran cantidad de situaciones que podemos probar, tenemos la posibilidad
de probar notificaciones sin la necesidad de crear un servicio en la nube que si es necesario al publicar la aplicación.

Manos a la obra!

Vamos a crear un proyecto básico pero suficiente para poder probar la
herramienta de notificaciones del emulador. Creamos un nuevo proyecto:

Partimos de la plantilla Blank App para centrar nuestra atención en la gestión de notificaciones. 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.

Para que la aplicación sea capaz de gestionar notificaciones Toast
debemos activar la opción en el archivo de manifiesto del paquete.

Pasamos a definir la interfaz de nuestra aplicación. El objetivo sera
probar la herramienta de notificaciones del emulador por lo que nuestra
interfaz sera lo más simple posible para conseguir el objetivo. En este
caso, un simple botón que al ser pulsado recuperare un canal de
notificación de inserción para la aplicación:

<Grid>
     <Button Content="Enviar notificación"
             Command="{Binding NotificationCommand}"
             HorizontalAlignment="Center"/>
</Grid>

El resultado es:

En la viewmodel correspondiente a la vista se ejecutará un comando:

private ICommand _notificationCommand;
 
public ICommand NotificationCommand
{
     get { return _notificationCommand = _notificationCommand ?? new DelegateCommandAsync(NotificationCommandDelegate); }
}
 
public async Task NotificationCommandDelegate()
{
     var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
 
     channel.PushNotificationReceived += (s, e) => {
          if (e.ToastNotification != null)
               Debug.WriteLine("{0}:{1}", e.NotificationType, e.ToastNotification.Content.InnerText);
     };
}

Utilizamos la clase PushNotificationChannelManager
encargada de crear objetos que se utilizan para recuperar canales de
notificaciones de inserción de Servicios de notificaciones de inserción
de Windows (WNS). Esta clase cuenta con un método llamado CreatePushNotificationChannelForApplicationAsync()
que sera el utilizado para recuperar un canal de notificación de
inserción para la aplicación. A continuación, para asegurarnos que todo
esta funcionando como debe, nos suscribimos al evento PushNotificationReceived que se desencadena cuando llega una notificación de inserción a este canal.

Simulando notificaciones

Y ya. Todo listo para poder probar el simulador de notificaciones!.
Pulsamos el botón y abrimos las herramientas extras seleccionando la
pestaña notificaciones:

Habilitamos la simulación de notificaciones y pulsamos el botón refrescar:

Se refresca el contenido de la herramienta mostrando el AppId de la aplicación asi como la Uri, tipo de notificación y plantilla
utilizada. En este punto podemos ver el contenido y probar con
distintos tipos de notificaciones hasta poder elegir con certeza cual
encaja con nuestros objetivos. Pulsamos el botón enviar:

Y automáticamente recibimos el mensaje de la notificación quedando la misma tambien registrada en el centro de actividades:

Podéis descargar el ejemplo realizado a continuación:

Más información

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

Introducció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:

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:

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:

¿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:

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 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"?>
  <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.

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.

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:

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:

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

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:

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

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:

El 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:

El 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

WPbeta.me. Nuevo servicio para gestión de betas de apps Windows Phone

Introducción

Como desarrolladores de aplicaciones Windows Phone debemos
esforzarnos por ofrecer aplicaciones atractivas y con funcionalidades
potentes y útiles para los usuarios. Sin embargo, debajo de todos esos
aspectos independientemente del tipo de aplicación que hagamos, debemos
ofrecer una experiencia fluida en todos los dispositivos con garantías
de calidad, es decir, sin errores (bugs). Para llegar a
este objetivo debemos cumplir múltiples pasos, desde la elección de una
buena arquitectura de la aplicación que nos permita crear una gran
cantidad de tests unitarios que cubran la mayoría de situaciones de la
aplicación y podamos cazar errores con rapidez a probar el resultado en
múltiples dispositivos para verificar rendimiento y otros aspectos. Dado
que no podemos tener todos los dispositivos disponibles en el mercado
(aunque nos gustaría), un movimiento recomendado a realizar son betas de nuestra aplicación.

Las betas nos permiten recibir feedback vital de rendimiento y
comportamiento de nuestra aplicación en distintos dispositivos asi como
feedback de usabilidad y otros bugs. Normalmente la gestión de betas las
realizamos con una beta privada para amigos y/o familiares o bien
buscamos testers en redes sociales u otros medios. En todos los casos la
situación es la misma. Necesitamos un correo (la cuenta Microsoft
utilizada por el usuario en su teléfono) por usuario.

¿Y como gestionamos todo esto?

Bien, el sistema no es único ni cerrado, podemos utilizar desde un
sencillo archivo de texto o un Excel hasta sistemas algo más complejos
pero más completos como por ejemplo MailChimp, sistema de gestión de
campañas de correo con el que mantener constancia de suscriptores,
campañas, etc.

NOTA: Tenéis disponible un gran artículo sobre la gestión de betas en el siguiente enlace al blog de Josué Yeray.

Hasta ahora…

WPbeta.me

Llega WPbeta.me, es un servicio totalmente gratuito creado por el MVP Domagoj Pavlesic destinado a facilitarnos la vida en la gestión de betas:

Como desarrolladores podemos utilizar WPbeta.me para facilitar todo el
proceso de gestión de betas. En el apartado App developers podremos
enviar nuestra aplicación para que pueda aparecer y ser testeado
sencillamente con su AppID:

NOTA: La aplicación debe estar publicada en la
Windows Phone Store como beta. El App ID podéis verlo en el Dashboard
del Windows Phone Dev Center, en los detalles de la aplicación.

Una vez enviada se obtendrán todos los metadatos de la aplicación creando una página de información como la siguiente:

La URL de la landing la podemos personalizar y mantenerla fácil para compartirla con amigos y/o familiares.

Como testers lo único que deben hacer es pulsar sobre el botón “Join this beta program” y autenticarse con su cuenta Microsoft. Tras esto ya tenemos disponible el email del tester en nuestro listado.

Un servicio recien sacado del horno aun en fase beta pero que ya
soluciona alguno de los problemas más importantes a los que nos podemos
enfrentar en la gestión de betas. Cualquier tipo de feedback, bug o
nueva característica se puede enviar con su formulario de contacto.
Asi que tanto si tenéis una aplicación de la que deseáis lanzar una
beta tanto si disfrutas ayudando a otros y probar a fondo aplicaciones,
tenéis disponible este nuevo servicio.

Más información