[XAML Tip] Validación de datos en Windows Phone 8 y Windows 8

Hola a todos!

Este fin de semana he estado trabajando en una forma sencilla y reutilizable de validar datos en aplicaciones Windows Phone 8 y Windows 8. Los que hayáis desarrollado aplicaciones en Silverlight o WPF, recordaréis que al establecer un Binding podíamos añadir un atributo ValidateOnException y ValidateOnDataError. Esto hacía que automáticamente el control se enmarcase en rojo cuando existía un error o si se lanzaba una excepción al establecer el valor a la propiedad de la ViewModel.

Lamentablemente en Windows Phone 8 y Windows 8 Microsoft no ha incluido estos atributos (en serio… a veces hacen cosas incomprensibles…). Aunque existían varias formas de validar los datos, ninguna era tan sencilla de usar desde XAML como las anteriores, así que me puse manos a la obra para crear un sistema de validación que nos permita informar al usuario de que existe un error en los datos. Este sistema tenía que cumplir varios requisitos.

1) En primer lugar debería permitirme definir cuando se va a llevar a cabo la validación:

  • Automáticamente al cargar el control (Evento Loaded)
  • Cuando el control pierda el foco (Evento LostFocus)
  • Cuando el usuario presione sobre el control (Evento Tap/Tapped)
  • Cuando el usuario presione una tecla o la suelte (Eventos KeyDown y KeyUp)
  • Cuando el texto del control cambie (Evento TextChanged)

2) Además, me debía permitir definir el tipo de validación a realizar:

  • El texto no está vacío.
  • El texto es un número valido.
  • El texto no es un número.
  • El texto tiene un formato determinado, usando expresiones regulares.

3) Por último, debía poder obtener el estado de la validación en mi ViewModel de alguna forma, para actuar en consecuencia.

Aunque existen algunos validadores en NuGet, ninguno cumplía todos mis requisitos. Así que cree un nuevo proyecto en NuGet y me puse manos a la obra a crear un validador para Windows Phone 8 y Windows 8. El resultado creo que ha quedado bastante bien y sobretodo es muy sencillo de usar. pongamos el caso de que deseo validar que un usuario ha introducido su nombre. La validación que quiero realizar es que el texto no esté vacío y el momento en el que deseo hacerla es cuando el control pierda el foco. No quiero que se valide nada más entrar a la página. Con el validador que he creado, podríamos hacer algo parecido a esto:

   1: <TextBox x:Name="textName">
   2:     <system:Interaction.Behaviors>
   3:         <val:TextBoxValidator ValidationFailed="{Binding NameValidationFailed, Mode=TwoWay}" 
   4:                               ValidateOnEvent="LostFocus" 
   5:                               ValidationPatternType="NotEmpty">
   6:         </val:TextBoxValidator>
   7:     </system:Interaction.Behaviors>
   8: </TextBox>

Hacemos uso de un Behavior llamado TextBoxValidator con tres propiedades:

  • ValidationFailed, esta propiedad boolean nos indicará si se ha fallado o no la validación, podemos enlazarla a una propiedad de nuestra ViewModel para obtener el estado de cada validación.
  • ValidateOnEvent, nos permite indicar en que momento deseamos que se dispare la validación, podemos escoger entre: KeyDown, KeyUp, Loaded, LostFocus, Tapped o TextChanged.
  • ValidationPatternType, indicamos que validación queremos llevar a cabo: IsNumeric, NotIsNumeric, NotEmpty o RegExPattern.

Una vez indicadas estas tres propiedades, podríamos añadir un TextBlock debajo de nuestro TextBox que muestre la validación y que solo sea visible cuando se falle la validación:

   1: <TextBlock Text="the name could not be empty." FontSize="24" Foreground="Red" Margin="12,0"
   2:            Visibility="{Binding NameValidationFailed, Converter={StaticResource BooleanToVisibilityConverter}}">
   3: </TextBlock>

El resultado de este XAML sería el siguiente:

image

Lo que mas me gusta es que no tenemos que añadir nada de lógica en code behind, todo funciona simplemente desde XAML sin que nos tengamos que preocupar de ello.

Otro caso interesante es el de validar entradas más complejas como, por ejemplo, un correo electrónico o un campo alfanumérico con un formato específico. Para esto, vimos que uno de los valores permitidos en la propiedad ValidationPatternType era RegExPattern.

Si indicamos este tipo, podremos indicar una cuarta propiedad llamada RegExPattern, en la que podremos indicar una expresión regular que el validador usará con el valor del texto. Si se encuentra al menos una coincidencia, se pasa la validación:

   1: <TextBox x:Name="textEmail">
   2:     <system:Interaction.Behaviors>
   3:         <val:TextBoxValidator ValidationFailed="{Binding EmailValidationFailed, Mode=TwoWay}" 
   4:                                 ValidateOnEvent="LostFocus" 
   5:                                 ValidationPatternType="RegExPattern" 
   6:                                 RegExPattern="^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$">
   7:         </val:TextBoxValidator>
   8:     </system:Interaction.Behaviors>
   9: </TextBox>
  10: <TextBlock Text="failed!" FontSize="24" Foreground="Red" Margin="12,0"
  11:            Visibility="{Binding EmailValidationFailed, Converter={StaticResource BooleanToVisibilityConverter}}">
  12: </TextBlock>

En este caso validamos un formato de correo electrónico, de forma sencilla y directamente desde XAML.

Si queréis usar estos validadores en vuestros proyectos, los podéis descargar desde NuGet directamente. Existe una versión para Windows Phone 8 y otra para Windows 8.

El uso de ambos es completamente igual. El mayor cambio es el soporte a los Behaviors. Mientras que en Windows Phone 8 se incluyen dentro de System.Windows.Interactivity, en Windows 8 tenemos que usar la librería WinRTBehaviors de mi buen amigo y Windows Phone MVP @Localjoost.

Si queréis ver el código fuente de ambos proyectos, podéis hacerlo desde Nokia Projects: https://projects.developer.nokia.com/input_validations, incluso podréis descargar el código usando GIT.

Por último, he creado dos ejemplos de como usar estos validadores, uno para Windows Phone 8 y otro para Windows 8, que también podéis descargar desde Nokia Projects: https://projects.developer.nokia.com/input_validations/files/downloads. Y con esto termino. Espero como siempre que os sea muy útil y que lo implementéis en muchos productos (y si lo hacéis, hacédmelo saber, principalmente por ego jejeje).

Un abrazo a todos y Happy Coding!!

 

 

 

[Windows 8] Tip: Saber si tenemos acceso real a internet

Hola a todos!

Una situación muy común hoy en día es encontrarnos con una conexión WiFi o Ethernet abierta en la que, una vez conectados, debemos acceder a un portal local para indicar un usuario y password. En este caso, efectivamente tenemos conectividad pero no tenemos salida a internet. Nos encontramos ante lo que se denomina una conexión cautiva.

Normalmente, comprobamos simplemente si tenemos internet o no. Para ello manejamos el evento NetworkStatusChanged de la clase NetworkInformation y consultamos el método GetInternetConnectionProfile. Si esta llamada nos devuelve NULL, entonces no tenemos internet. Si devuelve un ConnectionProfile, tenemos internet:

   1: if (NetworkInformation.GetInternetConnectionProfile() == null)
   2: {
   3:     ShowNotification("Conexión a internet desactivada.");
   4: }
   5: else
   6: {
   7:     ShowNotification("Conexión a internet activada.");
   8: }

Esto puede ser suficiente en muchos casos pero, como comentábamos al principio del artículo, puede que tengamos conexión de internet mediante una WiFi abierta. Cuando intentemos ejecutar una petición web o abrir una página web, lo primero que obtendremos será un portal interno de la red, pidiéndonos usuario y password. Esto es cada vez más normal en eventos y en hoteles. Con nuestro método anterior, tendríamos un falso positivo. Si, tenemos una conexión a internet, pero es inútil hasta que el usuario se autentifique en el portal. ¿Como podemos solucionar esto? Mediante el uso del método GetNetworkConnectivityLevel de la clase ConnectionProfile.

GetNetworkConnectivityLevel nos devolverá un enumerado con el nivel de conexión del perfil:

None, no disponemos de conectividad.

LocalAccess, acceso solo a red local, no a internet.

ConstrainedInternetAccess, nos informa de que…. nos encontramos tras un portal de autenticación!!, justo lo que necesitamos. Pero tiene su truco… luego lo veremos.

InternetAccess, acceso a internet y a la red local.

De esta forma, podríamos modificar nuestro primer código por algo así:

   1: public NetworkConnectivityLevel GetConnectivityLevel()
   2: {
   3:     var iConnection = NetworkInformation.GetInternetConnectionProfile();
   4:     if (iConnection == null)
   5:         return NetworkConnectivityLevel.None;
   6:  
   7:     return iConnection.GetNetworkConnectivityLevel();
   8: }

   1: var stat = this.networkService.GetConnectivityLevel();
   2:  
   3: switch (stat)
   4: { 
   5:     case NetworkConnectivityLevel.None:
   6:         ShowNotification("No existe conexión a internet.");
   7:         break;
   8:     case NetworkConnectivityLevel.InternetAccess:
   9:         ShowNotification("Existe conexión a internet.");
  10:         break;
  11:     case NetworkConnectivityLevel.LocalAccess:
  12:         ShowNotification("Solo acceso local.");
  13:         break;
  14:     case NetworkConnectivityLevel.ConstrainedInternetAccess:
  15:         ShowNotification("Debe autenticarse en el portal de la red.");
  16:         break;
  17: }

Ahora ya tenemos un control más granulado sobre el estado de nuestra conexión. Pudiendo indicar al usuario el estado exacto en el que se encuentra.

Pero no podemos confiar a ciegas en GetConnectivityLevel. Puede darse el caso en el cual nos encontremos en una conexión con un portal de autenticación y el estado devuelto sea LocalAccess, pues Windows no ha podido detectar el portal. Para estos casos la recomendación es que, siempre que nos encontremos en el estado LocalAccess, intentemos hacer una petición a una web pública conocida. Por ejemplo el archivo robots.txt de www.windowsphone.com (solo un ejemplo, aquí cada uno que vea la que prefiere). Al tratarse de un archivo plano, será muy fácil saber si nos encontramos tras un portal. Si la petición falla: no tenemos acceso a internet. Si la petición nos devuelve un archivo HTML, es muy posible que nos encontremos ante un portal y podamos informar al usuario de ellos.

Tendremos que tener en cuenta el caso concreto, esta es la recomendación oficial de MSDN para detectar un portal, depende de la situación, creo que en el estado LocalAccess podríamos informar al usuario de que existe la posibilidad de que:

A) tenga un problema con la conexión

B) Necesite autenticarse en un portal, ofreciéndole la opción de que abra un navegador y lo compruebe.

En ambos casos nos ahorramos la petición extra. Pero a cambio no podemos darle una respuesta concisa al usuario. Por eso comento que, depende de nuestras necesidades, puede hacer falta que realicemos la comprobación o solo con avisar nos llegará.

¿Y Windows Phone?

Pues… este código compila perfectamente en Windows Phone 8, pero no funciona. Si intentamos acceder al método GetNetworkConnectivityLevel desde un proyecto Windows Phone 8, obtendremos una excepción NotImplementedException, de esas que tanto gustan en Redmond… ¿Le pagarán a alguien a razón de cuantas NotImplementedException incluya en el API de Windows Phone? Esperemos que en futuras versiones, se reduzca el número de estas feas excepciones…

Y… FIN!

Esto es todo amigos! Un pequeño truco, para ayudarnos a crear apps que informen lo mejor posible al usuario sobre su conexión. A continuación os dejo un ejemplo de como aislar esta comprobación en un componente WinRT para poder usarlo tanto en C# como Javascript o C++ y reutilizarlo en diferentes proyectos. A disfrutarlo!

Un saludo y Happy Coding!

[Windows Phone 8] NFC y Bluetooth (1 de N)

Hoy vamos a ver una de las novedades incluidas en Windows Phone 8 que tenía pendiente de diseccionar en el blog: NFC.

nfc-logo

Siglas de Near Field Communications. Esta tecnología nos permite interconectar dos dispositivos tan solo con hacer que se toquen entre sí. De esta forma, no tenemos que introducir claves para emparejar dos dispositivos, ni debemos preocuparnos por que cualquiera que esté cerca de nuestro dispositivo pueda acceder a él, la comunicación solo se establece entre dos dispositivos a una distancia máxima de 4cm. Por esto es por lo que se considera una tecnología de contacto. A diferencia del bluetooth, que permite distancias mucho mayores.

NFC permite transferir a datos a una velocidad de hasta 424Kbps, con lo que intentar traspasar grandes cantidades de información puede ser muy costoso. Por esto, se puede usar en conjunción con otras tecnologías como el Bluetooth. En Windows Phone 8 podemos emparejar dos dispositivos mediante NFC y automáticamente enlazar sus respectivos bluetooths para usarlos como canal de transferencia de datos más pesados, como por ejemplo imágenes. De la misma forma, mediante NFC podemos acceder a datos almacenados en tarjetas, etiquetas o stickers NFC sin energía.

Windows Phone 8 nos aporta dos clases principales para trabajar con NFC: PeerFinder y Proximity Device. Cada una de ellas está especializada en un tipo de comunicación.

PeerFinder

PeerFinder nos permite descubrir otras instancias de nuestra aplicación en un dispositivo cercano y crear una conexión entre ambas instancias usando el gesto de tocar los dispositivos entre ellos o mediante browsing. Si el dispositivo con el que nos emparejamos no dispone de nuestra aplicación, el propio sistema le ofrecerá obtenerla de la tienda, con lo que puede ser una gran forma de que un usuario contento le pase nuestra app a otro. Si por el contrario la aplicación está instalada pero no iniciada, el sistema la iniciará automáticamente. Para realizar esto, solo deberemos llamar al método Start de la clase PeerFinder:

   1: public void FindPeerApp()
   2: {
   3:     Windows.Networking.Proximity.PeerFinder.Start();
   4: }

Realmente fácil!! Si unimos dos dispositivos y en uno de ellos ejecutamos este código, en el otro veremos lo siguiente (en el caso de no tener nuestra app):

image

Podemos ir un paso más allá, controlando el estado de la conexión con el evento TriggeredConnectionStateChanged. Este evento nos informará sobre el progreso de la conexión, mediante seis estados diferentes:

   1: public void FindPeerApp()
   2: {
   3:     PeerFinder.TriggeredConnectionStateChanged += PeerFinder_TriggeredConnectionStateChanged;
   4:     PeerFinder.Start();
   5: }
   6:  
   7: async void PeerFinder_TriggeredConnectionStateChanged(object sender, TriggeredConnectionStateChangedEventArgs args)
   8: {
   9:     switch (args.State)
  10:     { 
  11:         case TriggeredConnectState.Listening:
  12:             break;
  13:         case TriggeredConnectState.PeerFound:
  14:             break;
  15:         case TriggeredConnectState.Connecting:
  16:             break;
  17:         case TriggeredConnectState.Completed:
  18:             break;
  19:         case TriggeredConnectState.Canceled:
  20:             break;
  21:         case TriggeredConnectState.Failed:
  22:             PeerFinder.Stop();
  23:             break;
  24:     }
  25: }

Listening nos indica que nuestra aplicación ha iniciado la comunicación y estamos esperando que el otro dispositivo finalice la conexión.

PeerFound nos indica que existe otro dispositivo cerca.

Connecting indica que hemos recibido una solicitud de conexión, sin haberla iniciado nosotros.

Completed nos indica que hemos completado la conexión. A partir de este momento podremos usar la clase StreamSocket para comunicarnos con el otro dispositivo.

Canceled indica que la conexión ha sido cancelada.

Failed indica que un error evitó llevar a cabo la conexión.

Nos queda por mencionar que podemos usar nuestra aplicación Windows Phone 8 para anunciarnos a una aplicación Windows 8, simplemente indicando la propiedad AlternateIdentities de la clase PeerFinder:

   1: public void FindPeerApp()
   2: {
   3:     PeerFinder.AlternateIdentities.Clear();
   4:     PeerFinder.TriggeredConnectionStateChanged += PeerFinder_TriggeredConnectionStateChanged;
   5:     PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
   6:     PeerFinder.Start();
   7: }

Simplemente sustituyendo “Win8AppId” por el id de nuestra aplicación Windows 8, obtendremos el mismo resultado que entre dos Windows Phone. Si la aplicación está instalada en Windows 8, se lanzará. Si no está instalada, el usuario será llevado a la tienda para que pueda descargarla.

Hasta ahora hemos visto como “anunciar” nuestra aplicación al mundo exterior. Pero si queremos realizar una conexión punto a punto entre dos dispositivos, necesitamos primero obtener la lista de dispositivos a los que podemos acceder. Esto podemos hacerlo usando el método FindAllPeersAsync de la clase PeerFinder, que nos devolverá una lista con los dispositivos disponibles:

   1: public async Task<IReadOnlyCollection<PeerInformation>> FindPeers()
   2: {
   3:     return await PeerFinder.FindAllPeersAsync();
   4: }

Una vez que tengamos una lista de dispositivos, podremos conectar con uno en concreto usando el método ConnectAsync, que recibe un parámetro PeerInformation que hemos obtenido previamente con el método FindAllPeersAsync:

   1: public async Task<StreamSocket> ConnectToPeer(PeerInformation peer)
   2: {
   3:     return await PeerFinder.ConnectAsync(peer);
   4: }

En el dispositivo remoto, se lanzará el evento ConnectionRequested para que podamos identificar el dispositivo que se está conectando. Al mismo tiempo se lanzará el evento TriggeredConnectionStateChanged con el estado Completed. En este paso podremos obtener el SocketStream que se ha creado con la conexión.

Si la llamada a ConnectAsync tiene éxito, nos devolverá una instancia válida de la clase StreamSocket, que usaremos para recibir y enviar datos entre los dispositivos. Esta clase tiene tres propiedades importantes:

Information, nos ofrece información, como su nombre indica, sobre la conexión activa: la clave de sesión (SessionKey), estadísticas de ancho de banda, ip local y remota…

InputStream, contendrá la información enviada por el dispositivo remoto al que nos hemos conectado. Podremos obtener estos datos mediante el método ReadAsync del que dispone.

OutputStream, es donde escribiremos la información que queramos enviar al dispositivo remoto, usando el método WriteAsync.

NOTA: A la hora de conectar dos dispositivos con la clase PeerFinder es muy importante que el dispositivo que está escuchando, maneje el evento ConnectionRequested, de lo contrario la conexión se rechazará automáticamente.

Aunque el emparejamiento se lleva a cabo mediante NFC, cabe destacar que al invocar el método FindAllPeersAsync, se pasará a usar el bluetooth entre ambos terminales para llevar a cabo el envío y recepción de datos.

¿Como podemos probar todo esto?

Bien, la respuesta rápida: con dos dispositivos Windows Phone 8. Da igual si tienes un 920 y un 8X, un 620 y un Samsung Ativ S, o un 820 y un 920. Todos salvo el Lumia 520, incluyen NFC. De todas formas, reconozco que lo más normal es que no todo el mundo tenga dos dispositivos en su casa para probar alegremente. Si no tienes un par de Windows Phone 8, puedes probar parte de las conexiones, las que se realizan mediante NFC, haciendo uso de los emuladores y una aplicación llamada Proximity Tapper. Simplemente tendrás que instalar esta aplicación, asegurarte de que el firewall no la bloquea y dejarla iniciada. La pantalla se parece a esta:

image

*Al iniciar la aplicación, puede dar un error de UDP, le damos a aceptar y funciona correctamente. Aunque es ciertamente preocupante, el proyecto lleva sin actividad hace tiempo.

Entonces, despliega tu aplicación en dos emuladores de Windows Phone 8, por ejemplo el  WVGA y el WXGA, cuando los emuladores arranquen, se añadirán a la pantalla de proximity tapper sus IPs y nombre de dispositivo.

image

Y ya lo tenemos listo. Solo tenemos que seleccionar los dispositivos de la lista y presionar el botón Tap Selected Devices. Voila! los emuladores se emparejarán por NFC y si ejecutamos el método PeerFinder.Start en uno de ellos, el otro mostrará el aviso de la aplicación.

Lamentablemente, no he conseguido que funcione con el bluetooth, por lo que la llamada a FindAllPeersAsync o ConnectAsync, falla en los emuladores. Pero por lo menos podremos probar escenarios de emparejamiento con NFC, en el siguiente artículo hablaremos de ProximityDevice y como enviar mensajes sencillos usando solo NFC, sin Bluetooth o Wifi Direct y para eso nos vendrá de perlas este “emulador de NFC”.

Y Fin… Por ahora!!

Con esto, llegamos al fin del primer artículo sobre NFC. En próximos artículos hablaremos de como conectarnos a otros dispositivos, como usar la clase ProximityDevice y sobre el standard NDEF. Hasta entonces, os dejo el código fuente de la aplicación de ejemplo, integrando PeerFinder con MVVM.

Como siempre, un saludo y Happy Coding!!

[MATERIALES] Evento Mobility Day Madrid 2013

El pasado miercoles 22 de mayo estuve junto a mis compañeros de Plain Concepts Alfredo Fernandez, Javier Cantón y Rodrigo Díaz, en la Universidad Rey Juan Carlos de Móstoles. La idea del evento era contar un poco las novedades en desarrollo nativo para Windows Phone 8, de lo que me ocupé yo, desarrollo multiplataforma en PhoneGap, gracias a Alfredo, y desarrollo multiplataforma con Xamarin, de la mano de Javier y Rodrigo. Para terminar la mañana contamos con Oscar Gutierrez de Nokia Spain y Tiina Mutilainen de AppCampus Finland, que nos hablaron de los nuevos terminales y como conseguir financiación mediante AppCampus.

Después de comer nos pusimos manos a la obra con tres Workshops de 3 horas, en paralelo, sobre los temas de los que habíamos hablado por la mañana. Yo hice un workshop de Windows Phone 8, donde pudimos ver el entorno y los emuladores de Windows Phone, como implementar MVVM y como usar el GPS y los mapas. Alfredo hizo lo propio con PhoneGap y Javier y Rodrigo se hicieron un juego en vivo y en directo con Wave Engine, el motor de juegos multiplataforma que han hecho in house.

Fue un gran día, en compañía de buenos amigos, @alejacma, @tracker086, @gk_8, @tenientealdo o @jc_quijano se pasaron a visitarnos, con mucho networking y con muchas cosas que aprender. A continuación os dejo las Slides de mi charla sobre las novedades de Windows Phone 8:

También os dejo aquí el código fuente de los ejemplos que usé para que podáis trastear con ellos y usarlos para lo que necesitéis…

Y esto fue todo en el Mobility Day Madrid… a ver si nos vemos pronto en alguna otra ciudad.

Un saludo y Happy Coding!

Windows Phone Tip: Problemas al publicar para Windows Phone 7 y para Windows Phone 8

Hola a todos!

Hoy quiero hablar un poco de la publicación de aplicaciones para Windows Phone 7 y 8. Básicamente tendremos que tener dos XAP distintos, uno para cada plataforma. La teoría nos dice que el XAP que hayamos compilado para Windows Phone 7, se desplegará a dispositivos 7.X, mientras que el XAP que hayamos compilado para Windows Phone 8 se desplegará a dispositivos 8.X. Pero como todos sabemos, a veces la teoría falla y he visto muchas confusiones en este aspecto.

Existen dos cosas en particular que debemos tener muy en cuenta a la hora de publicar nuestros XAP: La versión del mismo y los idiomas soportados.

Versión del XAP

Para que un usuario con Windows Phone 8 reciba la versión adecuada, tenemos que indicar en el XAP compilado para Windows Phone 8 una versión superior a la del XAP de Windows Phone 7. En este aspecto debemos ser un poco cautos, si la versión de Windows Phone 7 es, por ejemplo, 1.5.0.42, no pongamos la versión de Windows Phone 8 1.5.0.43, porque si tenemos que desplegar una nueva versión del XAP para Windows Phone 7, pisaremos la versión y nos obligaremos a publicar ambas versiones. En este sentido creo que lo mejor es diferenciar muy claramente los números, por ejemplo teniendo la serie 1.X.X.X para Windows Phone 7 y 2.X.X.X para Windows Phone 8. De esta forma ambas aplicaciones podrán crecer y actualizarse sin pisar la una a la otra.

Idiomas Soportados

Este es uno de los temas más escabrosos y que más problemas a generado a la hora de mantener dos versiones para diferentes sistemas operativos. El marketplace de Windows Phone escoge la versión del XAP para nuestro dispositivo dándole mucha importancia a los idiomas soportados. En ambas versiones, 7.X y 8.X, debemos soportar exactamente los mismos idiomas. Si no coincide la lista de idiomas soportados, por ejemplo porque tengamos más idiomas en la aplicación Windows Phone 8, se enviará la versión de Windows Phone 7, aunque el idioma del dispositivo que se está instalando la app esté soportado en el XAP de Windows Phone 8. Debido a esto tenemos que ser muy cuidadosos con este punto. Antes de añadir un nuevo idioma a cualquiera de las dos versiones, estar seguros de que lo vamos a soportar en ambas y actualizar los dos XAP para evitar cualquier tipo de problema.

Esta forma de dar importancia a los idiomas se ha reportado a Microsoft como un punto importante de mejora. Independientemente de los idiomas soportados, debería pesar más la versión del sistema operativo que los idiomas soportados. En todo caso prefiero obtener la versión de Windows Phone 8 en inglés, que la versión de Windows Phone 7 en español ¿Nos harán caso? Tiempo al tiempo…

¿No tienes tu app publicada para ambas versiones?

¿De verdad? No me lo puedo creer. En Windows Phone 8 tienes acceso a nuevas APIs y controles, en algunos casos mejorando mucho la experiencia del usuario. No es tan difícil mantener dos versiones de tu aplicación si haces las cosas bien, aplicando el patrón MVVM correctamente, como enseño en este artículo.

Concluyendo…

Cuando te plantees soportar ambos sistemas, ten en cuenta estas dos reglas:

  • El número de versión del XAP de Windows Phone 8 siempre debe ser mayor que el de Windows Phone 7, usa series distintas: 1.X.X.X / 2.X.X.X.
  • Hasta que lo arreglen, si es que lo hacen, tienes que soportar los mismos idiomas exactos en ambas versiones, de lo contrario se enviará al usuario la versión de Windows Phone 7.

Observando estos dos puntos, no deberías tener ningún problema al publicar tu app y que tus usuarios reciban la versión correcta en su dispositivo.

Un saludo y Happy Coding!!

[EVENTO] Mobility Day Madrid el 22 de mayo

Hola a todos!

En Plain Concepts estamos preparando un gran evento sobre desarrollo para Windows Phone 8 que tendrá lugar el 22 de mayo en la Escuela técnica superior de ingeniería informática de la Universidad Rey Juan Carlos, en el campus de Móstoles. El evento durará todo el día, desde las 9 de la mañana a las 6 de la tarde.

Por la mañana, de 9:15 a 13:30 tendremos cuatro charlas (http://mobilityday.eventbrite.com/):

  • Desarrollo móvil nativo en Windows Phone 8, donde podremos ver las principales novedades de la plataforma y como implementarlas usando el patrón MVVM
  • Desarrollo móvil multiplataforma con PhoneGap, en la que repasaremos los fundamentos de la plataforma PhoneGap y como construir aplicaciones SPA con HTML5 y CSS3
  • Desarrollo móvil multiplataforma con C#, mostraremos como hacer uso de herramientas como Xamarin Studio para desarrollar nuestra aplicación para varias plataformas al mismo tiempo.
  • Nokia & AppCampus, recientemente la primera aplicación desarrollada en España ha sido aprobada en AppCampus y financiada con 50.000€ de inversión. Tendremos con nosotros a Tiina Muttilainen, responsable de AppCampus para España, que nos contará como podemos conseguirlo también nosotros con nuestras apps.

Después de ver todas las charlas, gracias a Nokia Spain y al programa Nokia Developer Champion, os invitaremos a comer y coger fuerzas para empezar con los Workshops que tendremos por la tarde.

Realizaremos tres workshops paralelos, desde las 15:00 hasta las 18:00:

NOTA: Tenéis que registraros de forma independiente a las charlas y al workshop al que queráis asistir. Podéis ir solo a las charlas o solo a un workshop, eso queda a vuestra elección.

Espero veros por allí a todos y que no desperdiciéis la oportunidad de ver de primera mano lo que Windows Phone 8 tiene que ofrecer y como aprovecharnos del desarrollo multiplataforma para crear nuestras aplicaciones y juegos.

Un saludo y Happy Coding!!

Materiales Master Class en RIATec

Hola a todos!

El pasado 8 de abril tuve la oportunidad de estar en Málaga con la gente del master RIATec hablando sobre Windows 8 y Windows Phone 8. En total tuvimos 4 horas para dar un repaso a las técnicas para compartir código entre Windows 8 y Windows Phone 8 (sin usar clases parciales ni enlace de archivos), ver los nuevos mapas de Windows Phone 8, las APIs de Voz y el uso de NFC.

Aquí van las slides que usé:

Compartir código entre Windows 8 y Windows Phone 8:

Nuevos mapas en Windows Phone 8:

APIs de voz en Windows Phone 8:

NFC en Windows Phone 8:

 

También aprovecho para dejaros los ejemplos de código que hicimos en la sesión, que espero os sirvan de ayuda. Podéis descargarlos aquí. La verdad es que fue todo un placer poder estar con los grandes cracks de Málaga, dar un paseito por la playa y luego tomar unas tapas por la noche. El año que viene amenazo con volver!!

Un saludo y Happy Coding!!

Resumen Megathon Windows 2013 en Tenerife

Hola a todos!

Como muchos sabéis, este fin de semana se ha llevado a cabo en 14 ciudades una nueva edición del Megathon. Básicamente hemos reunido ni más ni menos que ha 700 personas (más mentores y organizadores) y nos hemos pasado todo el fin de semana desarrollando apps para Windows 8 y Windows Phone 8.

Yo he tenido el placer de participar esta vez en Tenerife como mentor. Lo primero a destacar en esta ciudad, es la alta participación de grandes empresas, que apoyan regularmente este tipo de eventos. Panrico donuts nos ofreció un surtido de dulces para que la glucosa no bajase durante las largas horas de desarrollo, FuenteAlta nos regaló agua para todos y Natural Wok puso la nota oriental con un menú de tallarines y arroz acompañado con sushi que fue increíble (en calidad y en cantidad!!) FEMETE (La Federación de empresarios del metal y nuevas tecnologías de Tenerife) nos cedió totalmente gratis sus instalaciones, dos aulas para llevar a cabo el megathon y una zona de descanso donde comer y reunirnos en un ambiente más distendido. Por su parte, Vodafone nos cedió dos routers 3G para que nadie se quedase sin conectividad.

cafeteria

Desde luego a todos ellos hay que darles las gracias, son un ejemplo de apoyo a la creación de este tipo de eventos.

Me ha sorprendido gratamente la calidad de las apps en general que se han presentado y las ganas de la gente, alguno incluso no durmió el fin de semana más que unas pocas horas para tener su aplicación en la mejor forma posible para la presentación del domingo.

IMG_20130412_171405

Para cerrar un fin de semana redondo lleno de diversión y desarrollo, nos apuntamos al concurso de Harlem Shake que organizaba Nokia Spain, en el que regalaban un Lumia 520 para cada participante del video, que podéis ver a continuación:

Harlem Shake Tenerife 2013

Como siempre, ha sido un placer participar en el megathon, conocer gente nueva y ver grandes apps. Espero poder repetir el año que viene de nuevo!!

Un saludo y Happy Coding!

Empieza el Megathon 2013, enlaces interesantes

Hola a todos!

Hoy 12 de Abril comienza el Megathon Windows 2013, en esta edición se pueden presentar apps Windows Phone 8. Para que tengas toda la información posible a mano, aquí os traigo un resumen de enlaces interesantes, tanto sobre desarrollo, como sobre uso de máquinas virtuales o dispositivos remotos.

Antes de empezar con enlaces interesantes, daros a todos una buena noticia. Durante este fin de semana (12,13,14 y 15 de Abril) vamos a tener un descuento del 20% en el libro de Windows 8 y Windows Phone 8 que he escrito junto a Rafa e Ibon!! Podéis comprarlo en PDF y tener toda la información posible a mano, usando el cupón MEGATHON2013 obtendréis el 20% de descuento:

image

Vamos a por ello!

Probar apps y emulador de Windows Phone 8

Instalar el SDK de Windows Phone 8 en VMWare con soporte para el emulador

Probar aplicaciones en Dispositivos Físicos Remotos (Nokia RDA)

MVVM

Patrón MVVM (Video)

Compartir código entre Windows Phone 7 y Windows Phone 8

Desarrollo General Windows Phone 8

Fast App Resume en Windows Phone 8

Asociación de archivo y protocolos a nuestra aplicación

Gestión de contactos (escritura y lectura)

Uso de APIs de VOZ: Comandos, Reconocimiento y Sintetización

Interactuando con la pantalla de bloqueo

Uso de Nokia Here Places en Windows Phone 8

Mapas y localización en Windows Phone 8

Charla sobre mapas y lanzadores

Mapas y calculo de rutas

Geocoding y Reverse geocoding en Windows Phone 8

SDK Windows Phone 8

SDK de Windows Phone 8 en Visual Studio 2012

Windows 8

Windows 8 para desarrolladores Windows Phone 1 de 9

Windows 8 para desarrolladores Windows Phone 2 de 9

Windows 8 para desarrolladores Windows Phone 3 de 9

Windows 8 para desarrolladores Windows Phone 4 de 9

Windows 8 para desarrolladores Windows Phone 5 de 9

Windows 8 para desarrolladores Windows Phone 6 de 9

Windows 8 para desarrolladores Windows Phone 7 de 9

Windows 8 para desarrolladores Windows Phone 8 de 9

Windows 8 para desarrolladores Windows Phone 9 de 9

IoC & MVVM en Windows 8

Con estos enlaces, tenéis muchísima información para crear apps Windows Store y Windows Phone. Espero que os sea de ayuda.

Un saludo y Happy Coding!

Windows Phone 8: Here Places API

Hola a todos!

Hoy quiero aprovechar para hablar de la plataforma Here. Como sabréis, Here es la plataforma de mapas de Nokia, usando datos de NAVTEQ. En Windows Phone 8, tenemos múltiples aplicaciones que usan esta tecnología, Here Drive, Here Transit, Here Maps… e incluso podemos integrarla en nuestros propios desarrollos gracias al nuevo control de maps y las clases GeocodeQuery y ReverseGeocodeQuery, como ya vimos hace algún tiempo aquí y aquí. Pero existe un API extra, que no está implementada en el SDK de Windows Phone 8: Here Places.

Here Places nos permite acceder a los datos de localización de lugares de Nokia, de la misma forma que podemos hacer en Here Maps. Esto nos ofrece mucha información que poder incluir en nuestras aplicaciones, para enriquecerlas mucho más. Podremos encontrar información de 200 países, más de 1,5 millones de lugares (ciudades, distritos…) en total tendremos más de 200 millones de puntos de interés que podremos buscar y mostrar a nuestros usuarios.

Toda esta información se encuentra disponible a través de un servicio REST de Nokia, muy fácil de consumir desde nuestras aplicaciones Windows Phone. Pero para empezar, tendremos que registrarnos y obtener un Id de aplicación y un Código de aplicación.

Registro en Here Places

Para registrarnos, debemos acudir a http://developer.here.com donde podremos autenticarnos con el usuario y password que tengamos de Nokia developer. Tras autenticarnos, vamos a ir al menú “Create app”, donde necesitaremos indicar un nombre para la aplicación y una descripción:

image

A continuación presionaremos el botón “Get Started”, que nos llevará a la pantalla de selección de plan. Básicamente tendremos dos planes distintos para elegir: “Base”, que incluye mapas 2D ilimitados y 2.500 llamadas a otras APIs, como la de Places en este caso. Este plan es totalmente gratuito. El segundo plan es el “Core” cuesta $1.500 al mes, incluye 10.000 llamadas a otras APIs, pero seguro que se nos va de precio. Por ahora podemos quedarnos con el plan “Base” y presionar el botón “Select” que se encuentra al lado del mismo.

La última pantalla nos mostrará dos códigos: App Id y App Code, que deberemos incluir en todas las llamadas al API REST que realicemos:

image

Una vez que hayamos guardado nuestro Id y Code, podemos ir a Visual Studio y crear un nuevo proyecto de Windows Phone 8 para empezar a usar el API de Here Places.

Como ejemplo, vamos a hacer una aplicación que obtenga nuestra posición y nos devuelva los lugares cercanos disponibles. Además, como bonus, usaremos la clase TaskCompletitionSource, para convertir una clase que no usa async/await y trabajar más sencillamente con ella.

Para comenzar, necesitamos la URL del API REST de Places, exactamente una de estas dos:

Siempre que estemos desarrollando, debemos usar la URL que empieza por demo, dejando la otra solo para aplicaciones en producción. Una vez que sabemos que URL usar, vamos a crear un nuevo servicio en nuestra aplicación (asumo que, después de toda la chapa que he dado, estáis usando MVVM ;-P) que se llamará HereService y contendrá un método llamado GetNearbyPlaces y recibirá un objeto GeoCoordinate como parámetro.

public interface IHereService
{
    Task<VMPlace> GetNearbyPlaces(GeoCoordinate currentLocation);
}

public class HereService : IHereService
{
    private const string APP_ID = "YOURAPPID";
    private const string APP_CODE = "YOURAPPCODE";

    public Task<VMPlace> GetNearbyPlaces(GeoCoordinate currentLocation)
    {
        //TODO: Implement awesome code here!!!
    }
}

Una vez definido su Interface e implementación, vamos a empezar a trabajar. El método GetNearbyPlaces devuelve una Task<VMPlace>, pero nosotros usaremos para comunicarnos con el servicio REST la clase HttpWebRequest que no está preparada para async/await ni usa Tasks. De hecho, si os fijáis en el método GetNearbyPlaces, no he incluido la keyword async. Esto es porque no nos va a hacer falta, ya que el código que va a contener, no hace uso de async/await. Sin embargo el modelo de trabajo normal de HttpWebRequest se basa en callbacks. Por norma general, iniciaríamos la petición y cuando se terminase, llamaría a un callback desde el cual podríamos lanzar un evento para notificar el resultado, lo que nos da el tan conocido patrón Async/Completed.

Comenzaremos el código de nuestro método GetNearbyPlaces, construyendo la URI de la petición, básicamente esta debe contener las coordenadas, el ID de aplicación y el CODE de aplicación:

StringBuilder petitionUri = new StringBuilder("http://places.nlp.nokia.com/places/v1/discover/here?");
petitionUri.AppendFormat("at={0},{1}", currentLocation.Latitude, currentLocation.Longitude);
petitionUri.AppendFormat("&app_id={0}&app_code={1}&accept=application/json", APP_ID, APP_CODE);

Usamos la clase StringBuilder para evitar concatenar múltiples Strings, que crearían instancias en memoria, por un lado añadimos las coordenadas y en segundo lugar el ID y CODE de aplicación.

Una vez que hemos creado la URI, vamos a crear una instancia de la clase TaskCompletitionSource y devolver la Task que contiene como resultado de nuestro método:

public Task<VMPlace> GetNearbyPlaces(GeoCoordinate currentLocation)
{
    StringBuilder petitionUri = new StringBuilder("http://places.nlp.nokia.com/places/v1/discover/here?");
    petitionUri.AppendFormat("at={0},{1}", currentLocation.Latitude, currentLocation.Longitude);
    petitionUri.AppendFormat("&app_id={0}&app_code={1}&accept=application/json", APP_ID, APP_CODE);

    var taskCompletitionSource = new TaskCompletionSource<VMPlace>();

    return taskCompletitionSource.Task;
}

¿Para qué hacemos esto? Es muy sencillo. La teoría detrás de las Task es simple: En ejecución, al hacer un await sobre una Task, la aplicación espera a que esa Task tenga un Result, antes de continuar la ejecución del método que la esperó. Al devolver la Task del TaskCompletitionSource, devolvemos una Task no finalizada, sin resultado, por lo que el await esperará hasta que algo o alguien la complete. Esto lo haremos en el callback del método BeginGetResponse de la clase HttpWebRequest, como podemos ver a continuación:

HttpWebRequest request = HttpWebRequest.CreateHttp(petitionUri.ToString());
request.BeginGetResponse(async result =>
{
    if (result.IsCompleted)
    {
        VMPlace places = new VMPlace();
        taskCompletitionSource.SetResult(places);
    }
}, null);

Como podemos ver en el código anterior, creamos una expresión Lambda que recibe el resultado de la petición, la respuesta. Si está completada, creamos una nueva instancia de la clase VMPlace y usamos el método SetResult de TaskCompletitionSource para establecer el resultado. Es en este momento en el que la ejecución del método que originalmente llamó con await a GetNearbyPlaces, continúa, pues ya tiene un resultado y la Task se ha completado.

Pero no estamos devolviendo el resultado, simplemente devolvemos una instancia vacía. Primero, vamos a examinar el tipo de resultado que nos ofrece el API REST de Here Places:

{
    "results":
    {
        "next":"http://demo.places.nlp.nokia.com/places/v1/discover/search;context=Zmxvdy1pZD1iZDE0OTE1Yy1hMDAzLTU1OTEtYWU4ZC1iYjRlZjE3MGY0YmZfMTM2NDM2NjA5MTE4NV8wXzQ4MTMmb2Zmc2V0PTIw?app_id=APPID&app_code=APPCODE&size=20&q=restaurant&at=37.7851%2C-122.4047",
        "items":[
            {
                "position":[
                    37.785057,-122.404768
                ],
                "distance":8,
                "title":"Bin 55",
                "averageRating":5,
                "category":
                {
                    "id":"bar-pub",
                    "title":"Bares y clubes",
                    "href":"http://demo.places.nlp.nokia.com/places/v1/categories/places/bar-pub?app_id=_peU-uCkp-j8ovkzFGNU&app_code=gBoUkAMoxoqIWfxWA5DuMQ",
                    "type":"urn:nlp-types:category"
                },
                "icon":"http://download.vcdn.nokia.com/p/d/places2/icons/categories/22.icon",
                "vicinity":"55 Fourth Street<br/>San Francisco 94103<br/>EE.UU.",
                "having":[
                ],
                "type":"urn:nlp-types:place",
                "href":"http://demo.places.nlp.nokia.com/places/v1/places/8409q8yy-b29e8557ae0d4a1d8f0d7b7f9c10a8f0;context=Zmxvdy1pZD1iZDE0OTE1Yy1hMDAzLTU1OTEtYWU4ZC1iYjRlZjE3MGY0YmZfMTM2NDM2NjA5MTE4NV8wXzQ4MTMmcmFuaz0w?app_id=APPID&app_code=APPCODE",
                "id":"8409q8yy-b29e8557ae0d4a1d8f0d7b7f9c10a8f0",
                "references":
                {
                    "building":
                    {
                        "id":"9000000000000921352"
                    }
                }
            }
        ]
    },
    "search":
    {
        "context":
        {
            "location":
            {
                "position":[
                    37.7851,-122.4047
                ],
                "address":
                {
                    "house":"55",
                    "street":"4th St",
                    "postalCode":"94103",
                    "district":"Soma",
                    "city":"San Francisco",
                    "stateCode":"CA",
                    "county":"San Francisco",
                    "countryCode":"USA",
                    "country":"EE.UU.",
                    "text":"55 4th St<br/>San Francisco, CA 94103<br/>EE.UU."
                }
            },
            "type":"urn:nlp-types:place",
            "href":"http://demo.places.nlp.nokia.com/places/v1/places/loc-dmVyc2lvbj0xO3RpdGxlPTU1KzR0aCtTdDtsYXQ9MzcuNzg1MTtsb249LTEyMi40MDQ3O3N0cmVldD00dGgrU3Q7aG91c2U9NTU7Y2l0eT1TYW4rRnJhbmNpc2NvO3Bvc3RhbENvZGU9OTQxMDM7Y291bnRyeT1VU0E7ZGlzdHJpY3Q9U29tYTtzdGF0ZUNvZGU9Q0E7Y291bnR5PVNhbitGcmFuY2lzY287Y2F0ZWdvcnlJZD1idWlsZGluZw;context=Zmxvdy1pZD1iZDE0OTE1Yy1hMDAzLTU1OTEtYWU4ZC1iYjRlZjE3MGY0YmZfMTM2NDM2NjA5MTE4NV8wXzQ4MTM?app_id=APPID&app_code=APPCODE"
        }
    }
}

Nos encontramos ante una respuesta de tipo JSON. Para que sea fácil procesarla, podemos usar la librería NewtonSoft JSON, muy sencilla de utilizar y muy efectiva. Podemos instalarla usando NuGet. Una vez que la tengamos, crearemos la clase VMPlaces y la decoraremos con atributos para que pueda deserializar JSON de forma correcta:

public class VMPlace
{
    [JsonProperty("results")]
    public PlaceResult Result { get; set; }
}

public class PlaceResult
{
    [JsonProperty("next")]
    public string Next { get; set; }

    [JsonProperty("items")]
    public List<Place> Places { get; set; }
}

public class Place
{
    [JsonProperty("position")]
    public double[] Location { get; set; }

    [JsonProperty("title")]
    public string PlaceTitle { get; set; }

    [JsonProperty("distance")]
    public double Distance { get; set; }    
}

Simplemente creamos una jerarquía de clases que nos permita mapear los diferentes elementos y niveles del JSON. En este ejemplo está simplificado, nos faltarían más elementos y propiedades. Ahora ya podemos leer la respuesta usando HttpWebRequest y HttpWebResponse y mapear el JSON a nuestra clase usando NewtonSoft JSON:

if (result.IsCompleted)
{
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

    string resultJson;
    using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
    {
        resultJson = await reader.ReadToEndAsync();
    }
    if (!string.IsNullOrEmpty(resultJson))
    {
        VMPlace places = JsonConvert.DeserializeObject<VMPlace>(resultJson);
        taskCompletitionSource.SetResult(places);
    }
}

En primer lugar, obtenemos la respuesta con el método EndGetResponse de la petición HttpWebRequest que habíamos iniciado. A continuación obtenemos todo el texto usando un StreamReader sobre la Stream que devuelve el método GetResponseStream de HttpWebResponse. Una vez que tenemos nuestra string, solo tenemos que usar el método DeserializeObject de JsonConverter, indicando el tipo de objeto, y tendremos nuestra clase rellena con la información obtenida, ¿fácil, verdad?

Por último, ahora sí, llamamos al método SetResult de nuestro TaskCompletitionSource para terminar la tarea y hacer que devuelva el resultado de nuestra llamada al servicio. En definitiva, nuestro método GetNearbyPlaces quedaría de la siguiente forma:

public Task<VMPlace> GetNearbyPlaces(GeoCoordinate currentLocation)
{
    StringBuilder petitionUri = new StringBuilder("http://places.nlp.nokia.com/places/v1/discover/here?");
    petitionUri.AppendFormat("at={0},{1}", currentLocation.Latitude, currentLocation.Longitude);
    petitionUri.AppendFormat("&app_id={0}&app_code={1}&accept=application/json", APP_ID, APP_CODE);

    var taskCompletitionSource = new TaskCompletionSource<VMPlace>();
            
    HttpWebRequest request = HttpWebRequest.CreateHttp(petitionUri.ToString());
    request.BeginGetResponse(async result =>
    {
        if (result.IsCompleted)
        {
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

            string resultJson;
            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                resultJson = await reader.ReadToEndAsync();
            }
            if (!string.IsNullOrEmpty(resultJson))
            {
                VMPlace places = JsonConvert.DeserializeObject<VMPlace>(resultJson);
                taskCompletitionSource.SetResult(places);
            }
        }
    }, null);

    return taskCompletitionSource.Task;
}

Ahora solo nos queda por ver como llamamos al método, para asegurarnos de que, de cara al desarrollador, se comporta como un método basado en Tasks normal y corriente:

var result = await this.hereService.GetNearbyPlaces(this.myPosition);

if (result != null)
{
    Places = result;
    this.IsBusy = false;
}

Como podemos ver, nuestro método GetNearbyPlaces se usa como un método async más, y no implica ningún trabajo extra a la hora de consumirlo. De la misma forma podemos ver que usar el API REST de Here Places es realmente trivial y nos aporta muchísima información para nuestra aplicación.

En este ejemplo usamos HttpWebRequest, que sigue el antiguo patrón async/completed en vez de WebClient, que tiene métodos async/await basados en Task. ¿Porqué? No es gratuito, ni simplemente para lucirme o por que me guste escribir más código. WebClient está bien, pero no tiene el mismo rendimiento que HttpWebRequest. HttpWebRequest es más rápido y efectivo, además de consumir menos memoria, que WebClient. Esto se debe a que WebClient en realidad es solo un wrapper que utiliza HttpWebRequest / HttpWebResponse para realizar la petición y devolvernos el resultado ya procesado. Personalmente, considero que HttpWebRequest no supone un trabajo complicado y que usándolo en conjunción con TaskCompletitionSource, podemos tener una gran potencia, solo por escribir algunas líneas de código más. Así que no lo olvides:

WebClient es Evil, usa HttpWebRequest 

Conclusión

Y hasta aquí llega el artículo de hoy. Creo que Nokia está haciendo un gran trabajo con su plataforma Here y este API nos lo demuestra, si profundizáis más en el, usando el API Explorer, encontraréis mucha información interesante y muy útil. Como siempre, a continuación tenéis el proyecto de ejemplo que hemos estado viendo en el artículo, totalmente funcional y usando el patrón MVVM, espero que lo disfrutéis:

Un saludo y Happy Coding!!

Windows Phone 8: Interactuando con la pantalla de bloqueo.

Hola a todos!

Una de las novedades de Windows Phone 8 reside en su pantalla de bloqueo. En esta nueva versión del sistema operativo, podemos interactuar de una forma muy completa con la pantalla que se muestra al bloquear nuestro terminal. Podemos mostrar el icono de nuestra aplicación junto a un contador de notificaciones, mostrar texto e incluso cambiar la imagen de fondo.

¿Es esto algo que todas las apps deban implementar para ser más “cool”? No, ni mucho menos. Esta funcionalidad está pensada para ofrecer un mayor grado de personalización al usuario. Por ejemplo, si nuestra aplicación trabaja con imágenes, podemos darle al usuario la opción de usarla como proveedor de la imagen de fondo. Si nuestra app recibe notificaciones tile, quizás sea una buena idea que el usuario pueda configurar la pantalla de bloqueo para que muestre nuestras notificaciones. Incluso en una aplicación de mensajería, podríamos querer mostrar el último mensaje recibido en la pantalla de bloqueo (o no… eso queda a elección del usuario también).

Así, podemos distinguir tres formas de interactuar con la pantalla de bloqueo en Windows Phone 8:

  • Cambiando la imagen de fondo.
  • Mostrando el numero de notificaciones pendientes.
  • Mostrando un texto enviado por nuestra aplicación.

En este post, vamos a repasar cada una de ellas y ver como se implementa.

Cambiando la imagen de fondo

Esta es quizás una de las acciones más visuales, pues cambiamos totalmente el aspecto de la pantalla de bloqueo, cambiando su imagen de fondo.

Para ello, lo primero que necesitamos hacer en nuestro proyecto Windows Phone 8 es modificar el manifiesto de aplicación para añadir una nueva extensión. Esta extensión identificará a nuestra aplicación como proveedora de la imagen de fondo de la pantalla de bloqueo:

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

Una vez que hayamos añadido esta extensión, nuestra aplicación debería aparecer en el desplegable de Background de los settings de la pantalla de bloqueo.

image

Al seleccionar nuestra aplicación, tendremos un botón para lanzarla. La forma de establecer el fondo de la pantalla de bloqueo es muy sencilla. Podemos establecer una imagen que esté incluida en nuestra aplicación o que hayamos descargado y guardado en el isolated storage. Para definir de donde obtenemos la imagen, usaremos distintos esquemas URI:

  • ms-appx:/// Indica que la imagen está en el directorio de instalación de nuestra aplicación.
  • ms-appdata:///local/ Indica que la imagen está en el isolated storage de la aplicación.

Antes de poder establecer la imagen de fondo, tendremos que comprobar si tenemos permiso (el usuario ha seleccionado la aplicación en los settings) o no. Para ello comprobaremos la propiedad IsProvidedByCurrentApplication de la clase LockScreenManager que podemos encontrar en el namespace Windows.Phone.System.UserProfile. Si el resultado es false, podemos usar el método RequestAccessAsync de la misma clase para pedir permiso al usuario. Al ejecutar este método, se mostrará un popup del sistema informando al usuario de que deseamos proveer la imagen de fondo de la pantalla de bloqueo. Si acepta, como resultado obtendremos Granted, de lo contrario Denied. El código quedaría escrito de la siguiente manera:

var isProvider = Windows.Phone.System.UserProfile.LockScreenManager.IsProvidedByCurrentApplication;

if (!isProvider)
{
    var op = await Windows.Phone.System.UserProfile.LockScreenManager.RequestAccessAsync();

    isProvider = op == Windows.Phone.System.UserProfile.LockScreenRequestResult.Granted;
}

Una vez que hemos comprobado el acceso y pedido permiso al usuario si fuese necesario, ya solo nos queda llamar al método SetImageUri de la clase LockScreen, que se encuentra en el mismo namespace. Es importante que comprobemos que efectivamente tenemos permisos antes de llamar a este método, si lo hacemos y no tenemos permisos, el resultado será una excepción de seguridad. Finalmente, el método completo de nuestro código quedaría de la siguiente forma:

public async Task SetLockScreenWallpaper()
{
    var isProvider = Windows.Phone.System.UserProfile.LockScreenManager.IsProvidedByCurrentApplication;

    if (!isProvider)
    {
        var op = await Windows.Phone.System.UserProfile.LockScreenManager.RequestAccessAsync();

        isProvider = op == Windows.Phone.System.UserProfile.LockScreenRequestResult.Granted;
    }

    if (isProvider)
    {
        Windows.Phone.System.UserProfile.LockScreen.SetImageUri(new Uri("ms-appx:///newBackground.png"));
    }
}

Ahora ya podemos desplegar nuestra aplicación en el emulador. Al iniciarse, si hemos llamado al método SetLockScreenWallpaper que hemos creado, nos preguntará si deseamos permitir que la aplicación cambie el fondo de la pantalla de bloqueo. Si presionamos “Yes” (Si), y bloqueamos la pantalla del emulador y la volvemos a encender (pulsando F12 dos veces, se apaga y se vuelve a encender), veremos que ya tenemos nuestra imagen establecida.

image

Mostrando el número de notificaciones pendientes.

El segundo punto de personalización de la pantalla de bloqueo que podemos llevar a cabo, es mostrar las notificaciones pendientes, o cualquier otro número que deseemos, en la pantalla de bloqueo. Para ello, nuestro primer trabajo será crear un  icono de nuestra aplicación con un tamaño de 30x30 píxeles. En la documentación nos avisan de que este icono solo debe contener píxeles blancos y transparentes. Una vez que lo hayamos creado, tenemos que añadirlo al proyecto, estableciendo su propiedad Build Action en Content y Copy to output directory a Copy if newer.

A continuación tenemos que modificar el manifiesto de la aplicación para añadir una extensión, al igual que con el wallpaper, que indique al sistema que podemos establecer un icono y su contador en la pantalla de bloqueo:

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

También tendremos que modificar el nodo Token del manifiesto, exactamente modificaremos la línea DeviceLockImageUri, dentro del nodo PrimaryToken, para indicar la imagen que acabamos de crear:

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

Con estas modificaciones del manifiesto, si desplegamos la aplicación al emulador, podemos ir a los settings > Lock sceen y escoger nuestra app para mostrar notificaciones:

image

Como actualizamos el conteo de notificaciones pendientes? Muy fácil, el contador que se muestra en la pantalla de bloqueo es el mismo que el del tile principal de la aplicación. Simplemente actualizando el contador del tile, se actualizará la pantalla de bloqueo automáticamente:

public void UpdateTileCount(int count)
{
    ShellTile tile = ShellTile.ActiveTiles.First();

    FlipTileData data = new FlipTileData();
    data.Count = count;

    tile.Update(data);
}

Con este sencillo código, podemos actualizar el tile principal de la aplicación. Recuerda, que el tile principal siempre existe, aunque no hayamos anclado la app al inicio. Si ejecutamos la app y establecemos un nuevo número, al bloquear la pantalla veremos que se notifica:

image

Así de fácil podemos dar a nuestros usuarios más feedback sobre notificaciones o cualquier otra cosa que deseemos mostrar.

Mostrando un texto enviado por nuestra aplicación.

Y vamos con la última, pero no menos importante, forma de interactuar con la pantalla de bloqueo desde nuestra aplicación. La técnica es la misma que con el contador, lo primero que tendremos que hacer es modificar el manifiesto de nuestra aplicación para añadir una nueva extensión:

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

Una vez que hayamos incluido la extensión LockScreen_Notification_TextField, nuestra aplicación aparecerá listada en el desplegable de Settings > Lock screen > App to show detailed status. El texto que se mostrará se obtendrá desde el live tile principal de la aplicación, exactamente de la propiedad BackContent. Recuerda que no es necesario anclar la aplicación a la pantalla de inicio para poder establecer este texto, el tile principal siempre existe aunque no se muestre:

public void UpdateTileText(string text)
{
    ShellTile tile = ShellTile.ActiveTiles.First();

    FlipTileData data = new FlipTileData();
    data.BackContent = text;

    tile.Update(data);
}

Con la extensión y este simple método UpdateTileText, ya podremos comprobar la funcionalidad de nuestra aplicación. Para ello debemos desplegarla en el emulador o en un dispositivo e ir a Settings > Lock screen. En esta pantalla seleccionaremos nuestra aplicación en el desplegable App to show detailed status. Si antes o después de seleccionarla, establecemos la propiedad BackContent de nuestro tile, se reflejará también en la pantalla de bloqueo:

image

BONUS TRACK: Lanzar la pantalla de configuración de bloqueo desde nuestra aplicación.

Ahora que ya hemos visto como poder interactuar con la pantalla de bloqueo cambiando la imagen de fondo, añadiendo un icono de nuestra app con un contador o un texto de detalles, nos falta algo más por hacer. Sería estupendo si, ya que tenemos todas estas posibilidades implementadas en nuestra app, pudiésemos facilitar al usuario la configuración, dándole una forma rápida de ir a los settings de la pantalla de bloqueo desde nuestra app. Y de hecho, lo podemos hacer usando una funcionalidad nueva en Windows Phone 8: el lanzamiento de apps mediante URIs.

En el namespace Windows.System existe una clase llamada Launcher, que contiene un método llamado LaunchUriAsync. Este método nos permite lanzar al sistema cualquier URI y que sea este el que decida como ejecutarla. Por supuesto si lo que lanzamos es una URI de internet, http, https… se abrirá internet explorer. Pero si lanzamos una URI a la que se ha registrado una app, se lanzará esa aplicación.

Bien, pues muchas partes del sistema operativo están relacionadas con URIs exclusivas, como es el caso de la configuración de la pantalla de bloqueo. En este caso la URI asociada es “ms-settings-lock:” con lo que si añadimos el siguiente código a nuestra aplicación, se abrirá la pantalla de configuración de pantalla de bloqueo:

await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-lock:"));

Así de sencillo. Existen multitud de URIs interesantísimas como “ms-drive-to:” o “ms-walk-to:” que lanzarán la aplicación de navegación del sistema con la ruta calculada en coche o caminando que hayamos expresado.

Conclusión

Pues esto es todo, creo que si nuestra aplicación muestra información al usuario en su tile principal, integrarlo con la pantalla de bloqueo es un “must” que debemos cumplir. Además, según Windows Phone 8 vaya sustituyendo a Windows Phone 7.X, más y más usuarios buscarán aplicaciones que exploten todas las capacidades del dispositivo.

Si además nuestra aplicación trabaja de alguna forma con imágenes, poder establecer la pantalla de bloqueo es también algo increíblemente y que le da un punto diferenciador a nuestra aplicación.

Como siempre, a continuación os dejo los tres ejemplos completos y funcionales que hemos visto en este artículo para que podáis jugar con ellos:

Un saludo y Happy Coding!!

Windows Phone 8: Materiales del wphoneIO del 5 de marzo, MVVM

Hola a todos!

Ayer, junto a Alejandro Campos y Rafael Serna, tuve el placer de participar en una nueva edición de #wphoneIO, el hangout sobre desarrollo para Windows Phone que organiza desarrollo web. En esta ocasión centramos la hora entera de hangout en hablar sobre el modelo MVVM, Rafa nos explicó como funciona el enlace a datos y el DataContext en XAML y yo cree un ejemplo de implementación del patrón MVVM. Muy pocas diapositivas y mucho código, que es lo que al final se aprovecha.

Si no pudisteis verlo en directo ayer, podéis ver la grabación completa a continuación:

Además, aquí os dejo el proyecto con la implementación de MVVM que hicimos en vivo y en directo:

También, al final, tuve unos minutos para hablar sobre el programa de Nokia Premium Developer, las ventajas que ofrece y como podemos aprovecharnos de ellas, ya que creo que es un gran desconocido y nos puede ahorrar mucho dinero al registrarnos como publicador para Windows Phone o al actualizar una suscripción.

Espero que os sea útil, seguro que repetimos y volvemos a hacer una nueva edición con temas más avanzados, algún caso real y con tiempo para responder a todas las preguntas que tengáis.

Un saludo y Happy Coding!

Windows Phone 8: Probando nuestras aplicaciones en dispositivos físicos remotos

Hola a todos!

Hoy os traigo una utilidad que sin duda os ayudará y mucho en vuestros desarrollos. Una de las grandes barreras de entrada en Windows Phone 8 es la necesidad de que nuestro equipo cumpla unas características mínimas para poder usar los emuladores: SLAT, 64bits, Windows 8 Pro… esto evita que muchos podáis usar los emuladores. Otro problema que se plantea es la diferencia de configuraciones que podemos encontrar en dispositivos: Varias resoluciones, tamaño de memoria y de pantalla, almacenamiento interno…

Lo ideal siempre sería poder disponer de varios terminales para realizar las pruebas de la aplicación que estemos desarrollando, pero eso requiere una inversión muy alta de dinero por nuestra parte. Bien, pues Nokia viene al rescate, permitiéndonos probar en terminales físicos Lumia 620, 820 y 920 directamente desde la comodidad de nuestra casa, mediante su sistema RDA: Remote Device Access.

RDA consiste en un pool de dispositivos: Windows Phone, Symbian, S40, Meego, Belle… a los cuales podemos acceder de forma remota. No son emuladores vituales, son dispositivos físicos conectados a servidores que nos permiten usarlos a distancia. ¿Como podemos acceder a ellos? Bueno, el primer paso es registrarnos como desarrolladores nokia en developer.nokia.com,  un registro que es totalmente gratuito. Una vez hecho esto y estando logeados en nuestra cuenta, podemos acceder a la página de RDA. La primera vez que entremos, se validará que nuestro sistema cumple con las características necesarias para la conexión:

rda_02

Para usar el Remote Device Access, necesitaremos Javascript habilitado, un navegador compatible (no funciona con Chrome) y tener Java, al menos JRE 1.5, instalado. Si todo está OK, solo tendremos que pulsar en el botón Proceed y nos llevará directamente a la página de dispositivos:

image

Y lo primero que veremos serán los terminales disponibles, no siendo usados, en estos momentos. Con Windows Phone tenemos tres modelos, 620, 820 y 920. De cada modelo existen varios terminales, de forma que varias personas puedan usarlos al mismo tiempo. Podemos reservar el uso de un dispositivo por un periodo de tiempo de entre 15 minutos y 3 horas. Al pasar el ratón sobre cualquiera de ellos, veremos la información de reserva:

image

Solo tenemos que escoger el tiempo que deseamos usarlo y presionar Start. Si lo que queremos es asegurarnos de poder usar el dispositivo, por ejemplo esta tarde, podemos presionar el enlace de Make later reservation, que nos llevará a una página donde podremos planificar el uso del mismo:

image

Los dispositivos que aparecen en rojo están offline, los que aparecen normales están disponibles. De esta forma podemos planificar una sesión de pruebas con varios dispositivos y saber que estarán disponibles a la hora que los necesitamos.

Volviendo a la pantalla anterior, si presionamos el botón Start, empezaremos a usar nuestro terminal. Esto descargará un archivo jnrd a nuestro equipo y abrirá una aplicación Java en cliente.

image

Una vez aceptada esta advertencia y una confirmación del firewall de Windows, tendremos nuestro dispositivo listo para usar:

image

En este caso, un Lumia 620. Podemos navegar por internet, tenemos acceso a todo el dispositivo. En la barra de botones superior podemos Administrar los archivos, ver el log de consola, Instalar un XAP, Capturar imágenes, capturar video, Ir a pantalla completa…

Conclusión

Nokia RDA, es una solución ideal para probar nuestras aplicaciones en distintos dispositivos físicos de la casa Finlandesa. Por supuesto, en un mundo perfecto, lo mejor sería tener en nuestra oficina un modelo de cada Windows Phone que hay en el mercado para realizar pruebas exhaustivas. Pero sabemos que no estamos en un mundo perfecto y que la inversión en terminales sería gigantesca. Nokia RDA nos permite al menos acceder a la gama de terminales del fabricante finlandes, sin gastar un céntimo, lo cual es todo un avance.

Enlaces

Nokia Developer: https://www.developer.nokia.com/

Nokia Remote Device Access: https://www.developer.nokia.com/Devices/Remote_device_access/

Nokia Premium Developer Program: https://www.developer.nokia.com/Developer_Programs/Premium_program.xhtml

Un saludo y Happy Coding!

WPhoneIO: Patrón MVVM

Hola a todos!

WinPhone8Logo

Tras unas pequeñas vacaciones en las que he estado visitando Redmond con motivo del MVP Summit 2013 y Barcelona para el Mobile World Congress 2013, volvemos a la carga y que mejor que con un evento en vivo. Mañana martes 5 de Marzo a las 20:00 hora de España (GMT+1, 19:00 en Canarias)

En esta ocasión, dedicaremos el evento a hablar sobre el patrón MVVM, como implementarlo, porqué usarlo. Empezaremos viendo como se implementa y cerraremos con un debate acerca de su uso. Si estás empezando en el desarrollo sobre Windows Phone o llevas un tiempo, no te puedes perder este evento.

Todos los detalles, horarios en otras zonas y más información aquí: http://www.desarrolloweb.com/en-directo/modelo-mvvm-windows-phone8-7962.html

Un saludo y Happy Coding!

[Windows Phone 8] Compartiendo codigo en Windows Phone 7 y Windows Phone 8

Hola a todos!

Hoy vamos a ver un tema del que se ha hablado mucho en twitter últimamente. Con la aparición de Windows Phone 8, nos encontramos ante la duda de hacer una versión de nuestra aplicación para el nuevo sistema, abandonar Windows Phone 7.X o no desarrollar para 8, pues las aplicaciones 7.X funcionan perfectamente en la nueva versión del sistema.

Creo que ahora mismo, la respuesta es una mezcla entre ambas. Windows Phone 8 incorpora muchas mejoras que podemos aprovechar en nuestra aplicación: Comandos de voz, nuevos mapas, asociación de archivos, Fast App Resume, etc. Es una pena tener todo este potencial a nuestra disposición y no ofrecerlo al usuario. Por otro lado, Hay muchos terminales Windows Phone 7.X en la calle y es una pena no darles soporte. Lo que tenemos que intentar es conseguir que la mayor parte del código entre 7.X y 8.0 sea compartido.

Para ello nos aprovecharemos de un tipo de proyecto conocido como Portable Class Libraries. Estas librerías permiten compatibilidad con Windows Phone 7, 7.1 o 8.0, Windows Store, .NET, Silverlight e incluso XBox 360. Lo que vamos a intentar es trasladar a un proyecto Portable Class Library todo el código posible, dejando en cada proyecto Windows Phone solo el código exclusivo de cada plataforma. Además, pensando en que podamos expandir la aplicación a Windows 8 en un futuro, haremos la Portable Class Library compatible con aplicaciones Windows Store también.

Creando nuestra solución.

Lo primero que vamos a hacer es crear la estructura de proyectos. En este caso tendremos un proyecto Portable Class Library, que podemos encontrar en el nodo “Windows” de la pantalla de creación de nuevo proyecto. Este se llamará Common, pues contendrá el código común entre el resto de clientes. Al crear esta librería, se nos preguntarán los frameworks que deseamos soportar. Tenemos que seleccionar Windows Phone 7.5 o superior y Windows Store Apps. A continuación añadiremos dos proyectos de Windows Phone: Uno con la versión 8.0 y otro con la versión 7.1, no busquéis un proyecto exclusivo de Windows Phone 7.8, no existe.

Una vez que tengamos nuestros proyectos creados, vamos a añadir una referencia en los clientes Windows Phone al proyecto Portable Class Library, (Botón derecho sobre el proyecto, Add Reference: Solution y marcamos el proyecto Common). Con esto tenemos el cableado de nuestros proyectos hecho. Ahora tenemos que ocuparnos de algún otro asunto.

Async / Await en Windows Phone.

Windows Phone 8 soporta bastante bien el desarrollo asíncrono mediante el uso de async / await, pero Windows Phone 7.1 no lo soporta en absoluto. Esto puede llevarnos a tener que hacer auténticas peripecias para poder usar el mismo código en ambas plataformas. Además, como en nuestro proyecto portable hemos indicado que se usa Windows Phone 7.5 o superior, también podemos tener problemas. Para ayudarnos con estos y otros casos, Microsoft ha liberado en NuGet un paquete llamado BCL Portability Pack. Para encontrarlo, tendremos que habilitar en el administrador de NuGet que se incluyan los paquetes no estables, pues todavía se encuentra en estado Release Candidate.

image

De esta forma, podremos usar async / await o CallerMemberName en proyectos Windows Phone 7.5 sin ningún problema. También tendremos que añadir el segundo paquete que vemos, Async for .NET Framework 4, para que funcionen las llamadas usando async / await. Tendremos que añadir estos dos paquetes, tanto en el proyecto de Windows Phone 7.5 como en la Portable Class Library.

Empezando a cablear.

Vamos a comenzar a ensuciarnos las manos. Dicen que siempre hay que tener un objetivo claro, así que el nuestro va a ser hacer una aplicación que centre un mapa en nuestra posición. Para ello, lo primero que vamos a hacer es crear la infraestructura básica de MVVM en nuestra librería portable: VMBase, DelegateCommand y VMLocator.

¿Nunca has hecho una VMBase o un DelegateCommand y mucho menos un VMLocator? He creado unos pequeños Snippets de Visual Studio: VMBaseClass, DCommandClass y VMLAuto que se encargarán de crear las clases por ti, solo tienes que añadirlos a Visual Studio, crear una nueva clase y escribir VMBaseClass para la clase VMBase, darle dos veces a TAB para que se cree todo el código necesario y a continuación añadir los namespaces que falten, lo mismo para el Delegate Command o para el VMLocator. Puedes descargarlos aquí. Para el VMLocator además necesitarás descargarte la última versión de Autofac para librerías portables de aquí.

NOTA: La última versión 3.0 de Autofac es compatible con Portable class libraries, pero en las pruebas he visto algunos bugs al usarla en Windows Phone 7, la versión que indico (2.6.3 beta) funciona perfectamente. Podéis ver una conversación con Alex Meyer de Autofac aquí sobre este problema, que parece que no será corregido.

Al final tienes el ejemplo completo, que ya incorpora todo esto. Cuando hayamos terminado esta parte, debemos tener en nuestro proyecto Portable Class Library una carpeta ViewModels, con una subcarpeta Base que contenga tres clases: VMBase, VMLocator y DelegateCommand. Ahora vamos a modificar la clase VMLocator, por defecto se crea registrando tipos en el constructor, pero en nuestro caso queremos y necesitamos crear tipos  desde los proyectos Windows Phone, para añadir las dependencias de cada plataforma, quedando así:

public class VMLocator
{
    IContainer container;
    ContainerBuilder builder;

    public VMLocator()
    {
        this.builder = new ContainerBuilder();
    }

    public void RegisterType(Type Tto, Type TFrom)
    {
        this.builder.RegisterType(Tto).As(TFrom);
    }

    public void BuildContainer()
    {
        this.builder.RegisterType<VMMainPage>();

        this.container = this.builder.Build();
    }
}

Más adelante, llamaremos a los métodos RegisterType y BuildContainer desde nuestra aplicación Windows Phone para construir las dependencias necesarias. Antes de empezar a trabajar en nuestra ViewModel, vamos a crear en la librería portable una carpeta llamada Entities en la que crearemos una clase llamada Coordinates:

public class Coordinates
{
    public double Latitude { get; set; }

    public double Longitude { get; set; }

    public double? Altitude { get; set; }
}

¿Para que sirve esta clase Coordinates? Bien, resulta que la librería portable permite acceder al framework de .NET que mejor se ajuste a todas las plataformas que hemos escogido al crearla, pero solo eso, .NET puro. Lamentablemente los tipos de datos GeoCoordinate, Geocoordinate y demás, son tipos especiales del framework de Windows Phone, por lo que no están accesibles en la librería. Lo que hemos hecho es crear nuestro propio objeto Coordinate, para recibir los datos.

A continuación crearemos una carpeta Services y dentro un interfaz llamado IGpsService con un método llamado GetCurrentPosition cuya implementación realizaremos más adelante:

public interface IGpsService
{
    Task<Coordinates> GetCurrentPosition();
}

Ahora podemos añadir este servicio a nuestra ViewModel y crear la lógica que, al pulsar un botón el usuario, obtenga las coordenadas actuales y las exponga en una propiedad GpsCoordinates:

public class VMMainPage : VMBase
{
    private IGpsService gpsService;

    private DelegateCommand getCoordinatesCommand;

    private Coordinates gpsCoordinates;

    public VMMainPage(IGpsService gpsService)
    {
        this.gpsService = gpsService;

        this.getCoordinatesCommand = new DelegateCommand(ExecuteGetCoordinatesCommand);
    }

    public ICommand GetCoordinatesCommand
    {
        get { return this.getCoordinatesCommand; }
    }

    public Coordinates GpsCoordinates
    {
        get { return this.gpsCoordinates; }
        set
        {
            this.gpsCoordinates = value;
            RaisePropertyChanged();
        }
    }

    private async void ExecuteGetCoordinatesCommand()
    {
        GpsCoordinates = await this.gpsService.GetCurrentPosition();
    }
}

De esta forma hemos completado el código de nuestra ViewModel. Pero, ¿Donde implementamos el código de la interfaz IGpsService? Pues lo haremos en cada una de las aplicaciones cliente. Esto tiene que ser así por qué, mientras Windows Phone 7 usa la clase GeoCoordinateWatcher para acceder al GPS, en Windows Phone 8 usamos la nueva clase GeoLocator de WinPRT. Lo que vamos a hacer es, en el proyecto Windows Phone 7, crear una carpeta Services y una clase GpsService que implemente la interfaz IGpsService:

public class GpsService : IGpsService
{
    public Task<Coordinates> GetCurrentPosition()
    {
        TaskCompletionSource<Coordinates> completitionTask = new TaskCompletionSource<Coordinates>();

        GeoCoordinateWatcher watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
        watcher.PositionChanged += (o, e) =>
            {
                watcher.Stop();
                if (e.Position != null)
                    completitionTask.SetResult(new Coordinates()
                    {
                        Latitude = e.Position.Location.Latitude,
                        Longitude = e.Position.Location.Longitude,
                        Altitude = e.Position.Location.Altitude
                    });

            };
        watcher.Start();

        return completitionTask.Task;
    }
}

Debemos recordar que en Windows Phone 7.X no existía async / await, por lo que tendremos que usar la clase TaskCompletitionSource<T> para envolver nuestras llamadas y cumplir la interfaz IGpsService. En Windows Phone 8, que ya soporta async / await, la implementación será más sencilla:

public class GpsService : IGpsService
{
    public async Task<Coordinates> GetCurrentPosition()
    {
        Coordinates result = new Coordinates();
        Geolocator locator = new Geolocator();
        locator.DesiredAccuracy = PositionAccuracy.High;
        locator.MovementThreshold = 10;

        var currentPosition = await locator.GetGeopositionAsync();
        if (currentPosition != null)
        {
            result = new Coordinates()
            {
                Latitude = currentPosition.Coordinate.Latitude,
                Longitude = currentPosition.Coordinate.Longitude,
                Altitude = currentPosition.Coordinate.Altitude
            };
        }

        return result;
    }
}

Con esto, hemos implementado de forma nativa en cada plataforma el acceso al GPS, pero ambas cumpliendo la interfaz que hemos definido en nuestra librería portable. Esto nos permitirá, usando Autofac, inyectar cada implementación cuando sea preciso. Para ello, en primer lugar, vamos a añadir nuestro VMLocator como un recurso en el archivo app.xaml de cada aplicación:

<Application.Resources>
    <locator:VMLocator x:Key="Locator"></locator:VMLocator>
</Application.Resources>

En segundo lugar, vamos a modificar el código de la clase App, app.xaml.cs. Exactamente la función InitializePhoneApplication, para recuperar el locator que hemos definido en xaml, registrar la implementación del GPS y construir el contenedor de dependencias. Todo esto debemos realizarlo antes de navegar a la primera página:

private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;

    VMLocator locator = (VMLocator)App.Current.Resources["Locator"];
    locator.RegisterType(typeof(GpsService), typeof(IGpsService));
    locator.BuildContainer();

    // Create the frame but don't set it as RootVisual yet; this allows the splash
    // screen to remain active until the application is ready to render.
    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;

    // Handle navigation failures
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    // Handle reset requests for clearing the backstack
    RootFrame.Navigated += CheckForResetNavigation;

    // Ensure we don't initialize again
    phoneApplicationInitialized = true;
}

Y ya solo nos queda construir la interfaz de usuario, muy simple en este ejemplo. Colocaremos un botón que se encargue de invocar a nuestro comando GetCoordinatesCommand y unos textblocks que muestren las coordenadas obtenidas:

<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Button Content="Get coordinates"
            Command="{Binding GetCoordinatesCommand}"></Button>
    <TextBlock Text="Latitude:"></TextBlock>
    <TextBlock Text="{Binding GpsCoordinates.Latitude}"></TextBlock>
    <TextBlock Text="Longitude:"></TextBlock>
    <TextBlock Text="{Binding GpsCoordinates.Longitude}"></TextBlock>
    <TextBlock Text="Altitude:"></TextBlock>
    <TextBlock Text="{Binding GpsCOordinates.Altitude}"></TextBlock>
</StackPanel>

Si todo marcha como es debido, ya deberíamos poder ejecutar ambas versiones y verlas en funcionamiento como se muestra a continuación.

image

Conclusión

¿Es posible compartir código entre Windows Phone 7 y 8? Si, pero no todo. Con una buena implementación de MVVM, usando un contenedor de dependencias y pensando un poco en lo que deseamos hacer, podemos compartir la mayor parte de la lógica de la aplicación. También podríamos compartir parte del XAML, quizás, pero puede que el esfuerzo fuese mayor que el beneficio. En definitiva, hemos visto como poder compartir toda la infraestructura de MVVM, y nuestras ViewModels, así como los contratos de los servicios que implementará cada plataforma. Es muy importante que los servicios sean lo más “tontos” posible. Que simplemente devuelvan datos y cualquier tipo de lógica se lleve a cabo en la ViewModel, para evitar duplicar más código del necesario.

Como siempre, a continuación tenéis el ejemplo que hemos desgranado en el artículo para que lo descarguéis. Cualquier sugerencia, comentario, crítica será bien recibida.

Un saludo a todos y Happy Coding!!

[Windows Phone & Windows 8] Campaña I APP YOU

Hola a todos!

Hoy vengo con un post muy cortito para informaros sobre una nueva campaña que Microsoft España a lanzado: I APP YOU.

728x90-iapp 

Básicamente, aunque se explica mas a fondo en la página de la campaña, tenéis entre el 1 de febrero y el 15 de mayo para crear una app para Windows Phone o para Windows 8 y publicarla en su correspondiente Store. Entre todas las aplicaciones que se publiquen, se sortearán 2 XBOX 360 SEMANALES con lo que las posibilidades de ganar una, son muchas.

Solo me gustaría hacer una puntualización. Por favor, el premio es jugoso y puede tentarnos la idea de hacer aplicaciones como churros. Con no demasiada calidad, solo por el echo de publicar rápido y ganar una XBOX 360… los que hagáis esto, puede que disfrutéis de la consola, pero estáis destruyendo la plataforma e inundándola con malas apps. Publicad buenas apps, bien realizadas, siguiendo patrones y buenas prácticas. Y sobre todo originales, no hagáis apps clónicas…

700667091

Mucha suerte a todos y Happy Coding!!

Windows Phone 8 Tip: Fast App Resume

Hola a todos!

Hoy vengo con un pequeño truco, que mejorará increíblemente la experiencia de uso de nuestra aplicación. Se trata de una nueva característica de Windows Phone 8 llamada Fast App Resume.

Pero… ¿Qué es esto exactamente? Bueno, desde Windows Phone 7.5, sabemos que nuestra aplicación queda suspendida cuando el usuario va a la pantalla inicial. Si presionamos el botón atrás durante unos segundos, aparece el gestor de aplicaciones, donde vemos todas las aplicaciones abiertas (y suspendidas) en ese momento, podemos pulsar sobre una y automáticamente se vuelve a activar. Esto es lo que conocemos como FAS, Fast Application Switching. Pero si, en vez de usar este método, el usuario vuelve a pulsar sobre el icono de la aplicación o el Tile, la instancia suspendida es eliminada de memoria y se crea una nueva instancia. Esto, por supuesto, es mucho más lento que recuperar una instancia ya creada. Para solventar este inconveniente, Windows Phone 8 soporta además de FAS, un nuevo modo llamado FAR, Fast Application Resume. FAR nos permite indicar al sistema que deseamos mantener la instancia actualmente creada aunque el usuario pulse sobre el Tile principal de la aplicación. De esta forma solo se tiene que activar la instancia y no destruirla y crearla de nuevo. Por supuesto esto es muchísimo más rápido. Si estás mirando que características añadir a una aplicación migrada desde Windows Phone 7.5 a 8.0, esta es indispensable.

FAR: Fast Application Resume

¿Y como lo activamos? Aquí viene la mejor parte, solo tenemos que modificar una línea del XML del manifiesto de nuestra aplicación para activarlo. Tenemos que buscar el elemento DefaultTask y añadirle el atributo ActivationPolicy:

<Tasks>
  <DefaultTask  Name ="_default" NavigationPage="MainPage.xaml" ActivationPolicy="Resume"/>
</Tasks>

Este atributo ActivationPolicy puede recibir dos valores: Resume o Replace. Resume activa el Fast Application Resume, indicando que siempre que sea posible la aplicación debe recuperar la instancia activa. Replace es el valor por defecto si no indicamos Resume y genera el comportamiento normal, la aplicación inicia una nueva instancia.

¿Como podemos comprobar la diferencia entre ambos modos? Podemos verlo de forma muy sencilla que nos ilustrará los efectos del FAR. Vamos a crear una aplicación Windows Phone 8 que tenga un TextBox en su página principal. Sin indicar el valor del ActivationPolicy, hagamos los siguiente pasos:

  1. Desplegamos en el dispositivo o emulador.
  2. Escribimos “Hola app” en el TextBox.
  3. Presionamos el botón inicio.
  4. Vamos a la lista de aplicaciones y pinchamos de nuevo sobre nuestra aplicación.

¿Cuál es el resultado? El texto “Hola app” ha desaparecido, bueno, en realidad nunca estuvo escrito, puesto que al pinchar sobre la aplicación, se ha destruido la instancia suspendida (donde estaba el texto) y se ha creado una nueva. Vamos a repetir la prueba, solo que ahora, modificaremos nuestro manifiesto para añadir el atributo ActivationPolicy=”Resume”. ¿Cual es el resultado? Efectivamente, el texto permanece en su sitio, puesto que lo que hemos realizado es recuperar la copia en memoria.

Cambios en la navegación

Aunque, como hemos visto, implementar FAR es muy sencillo y sus ventajas son obvias, queda un asunto por discutir: la navegación. ¿Qué experiencia de navegación deseamos ofrecer al usuario? Dependiendo de como activemos la aplicación, cambiará la experiencia de navegación que obtendremos:

  • Si activamos la aplicación desde un tile o desde la lista de aplicaciones: Se reiniciará la pila de navegación y a continuación se realizará una navegación a la página que corresponda.
  • Si volvemos al la aplicación con el botón atrás o desde la lista de apps abiertas: Volveremos a la página abierta, conservando la pila de navegación.

Esta diferencia se da debido a que, al navegar desde un tile o la lista de aplicaciones, recibimos dos navegaciones. La primera de ellas va dirigida a la página principal de nuestra aplicación con el modo de navegación establecido en Reset. Este modo es manejado por el método CheckForResetNavigation de la clase App (App.xaml.cs) y se encarga de limpiar la pila de navegación:

private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
    // If the app has received a 'reset' navigation, then we need to check
    // on the next navigation to see if the page stack should be reset
    if (e.NavigationMode == NavigationMode.Reset)
        RootFrame.Navigated += ClearBackStackAfterReset;
}

private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
    // Unregister the event so it doesn't get called again
    RootFrame.Navigated -= ClearBackStackAfterReset;

    // Only clear the stack for 'new' (forward) and 'refresh' navigations
    if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
        return;

    // For UI consistency, clear the entire page stack
    while (RootFrame.RemoveBackEntry() != null)
    {
        ; // do nothing
    }
}

A continuación recibimos una segunda navegación, esta vez a la página que deseamos llegar y con el modo establecido en New. De esta forma, aunque hemos recuperado la instancia activa de la aplicación, que es mucho más rápido que abrir una nueva, tenemos la impresión de estar ante una nueva instancia. Pero puede ocurrir que deseemos ofrecer una experiencia diferente al usuario, haciendo que al pulsar un tile principal o la lista de aplicaciones, vuelva a la página activa antes de suspender la aplicación. Para esto tendremos que controlar la forma en la que se está iniciando la aplicación. Sabemos que en el evento CheckForResetNavigation un modo de Reset significa que se inicia desde un tile o lista de aplicación y que a continuación navegará a la página indicada, poniéndola encima del resto en la pila de navegación. podríamos modificar el CheckForResetNavigation para hacer algo parecido a esto:

private bool loadingfromInstance = false;

private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
    if (e.NavigationMode == NavigationMode.New && this.loadingfromInstance)
        RootFrame.GoBack();

    // If the app has received a 'reset' navigation, then we need to check
    // on the next navigation to see if the page stack should be reset
    if (e.NavigationMode == NavigationMode.Reset)
        this.loadingfromInstance = true;
}

De esta forma, en la primera navegación con el modo Reset, no eliminamos la pila de navegación y marcamos a true la variable loadingFromInstance. En la segunda navegación, con el modo New, controlamos que previamente hayamos recibido un Reset. Si es así, navegamos atrás, para volver a la página en la que dejamos la aplicación. De cara al usuario, el resultado es recuperar la aplicación en el mismo punto exacto en que la dejó y con la misma pila de navegación. Un ejemplo de este comportamiento lo tenemos en la aplicación de Facebook para Windows Phone 8, que se comporta exactamente de esta manera.

Pero con este código, veremos que tenemos un inconveniente. Primero se carga la página principal y a continuación se navega hacia atrás y se muestra la página en la que nos encontrábamos, haciendo un efecto bastante feo. Para arreglar esto, debemos modificar el método InitializePhoneApplication para añadir un manejador al evento Navigating del objeto RootFrame. En este manejador, comprobaremos si estamos navegando a la página principal y si loadingFromInstance es true. Si se cumplen ambas condiciones, cancelaremos la navegación.

Es importante que volvamos a establecer loadingFromInstance a false una vez que realicemos este paso o cualquier tipo de navegación a la página principal será cancelada:

void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
    if (e.Uri.ToString().Contains("MainPage.xaml") && this.loadingfromInstance)
    {
        e.Cancel = true;
        this.loadingfromInstance = false;
    }
}

De esta forma, no necesitamos hacer el GoBack en nuestro método CheckForResetNavigation, simplemente no navegamos a la página principal si estamos recuperando la instancia, quedando el código de ese método mucho más sencillo como podemos ver a continuación:

private bool loadingfromInstance = false;

private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
    if (e.NavigationMode == NavigationMode.Reset)
        this.loadingfromInstance = true;
}

Y Voila! Ya tenemos nuestra aplicación recuperando la instancia anterior y dejando al usuario en el mismo sitio exacto en el que estaba.

Conclusión

El FAR o Fast Application Resume en Windows Phone 8 es una de las novedades más útiles y a la vez más pasada por alto que nos ofrece la nueva versión del sistema operativo. Podemos ofrecer al usuario una experiencia de recuperación de estado mucho mejor y una velocidad de inicio de nuestra aplicación mayor, usando este simple truco. En nuestras manos queda como manejar la pila de navegación, si queremos ofrecer una experiencia de continuidad o simplemente aprovecharnos de la rapidez de inicio y reiniciar la navegación para que se asemeje a un arranque desde cero.

Como siempre, a continuación os dejo un pequeño ejemplo del uso de FAR, con una aplicación que contiene dos páginas y nos permite crear un tile secundario para probar la navegación desde diferentes puntos.

Un saludo y Happy Coding!

Grupo de desarrolladores hispanos de Windows Phone 8 en G+

Hola a todos!

image Oscar Gutiérrez de Nokia Spain ha creado un grupo de desarrolladores en Google plus para Windows Phone 8. Tendremos las últimas noticias por parte de Nokia, tutoriales, artículos e incluso algunas charlas en vivo sobre la plataforma. Creo que es un recurso increíble para mantenerte en contacto con la comunidad y quería compartirlo con todos!

Si te interesa el desarrollo para Windows Phone 8, no puedes dejar de pasar por aquí
Publicado por Josué Yeray Julián Ferreiro con no comments
Archivado en: ,

[Evento] Windows Phone 8 & PhoneGap en Barcelona

Hola a todos!

Plain Conceptslogonokia

WinPhone8Logophonegaplogo

¿Qué ocurre cuando, en una misma sala unes a Plain Concepts, Nokia y el Google Developers Group de Barcelona? Pues que se lía… eso os lo aseguro!!

Y como nos gusta liarla, el 11 de Febrero, desde las 9:30 de la mañana hasta las 18:00 estaremos en Barcelona Alfredo Fernández, Gerard López y un servidor por parte de Plain Concepts y Oscar Gutiérrez de Nokia Spain, para hacer un evento sobre Windows Phone 8 y las novedades que nos ofrece y lo fácil que es desarrollar para la plataforma. Pero queríamos ir un paso más allá y ver las posibilidades que nos brindan HTML5 y Javascript para desarrollar aplicaciones multiplataforma. ¿Quién mejor que Alfredo Fernandez y Gerard Lopez para enseñarnos las bondades de PhoneGap? Entre los dos, ya han publicado apps profesionales para Android, iOS y BlackBerry desarrolladas totalmente en PhoneGap, aplicaciones con acceso a servicios externos, uso de propiedades del sistema, multi resolución, con uso de Media queries y demás trucos modernos de CSS3.

Además, gracias a la colaboración de Oscar Gutiérrez de Nokia Spain tendremos Algunos dispositivos con Windows Phone 8 para que podáis probar código en ellos y ver como funcionan y algunas sorpresas más…

Si os apetece pasar un día divertido, desarrollando y conociendo los últimos detalles sobre XAML + C#, HTML5/JS y el desarrollo nativo/multiplataforma en Windows Phone 8 y PhoneGap, no podéis dejar de venir. 

Aquí tenéis el registro del evento que, por supuesto y de la mano de Plain Concepts, es totalmente gratuito!!

Espero veros a todos, pasaremos lista!

Un saludo y Happy Coding!

Windows Phone 8: Asociación de archivos y protocolos

Hola a todos!

Una de las novedades de Windows Phone 8 es la capacidad de asociar nuestra aplicación a tipos de archivos. De esta forma, cuando se intente acceder a un tipo de archivo para el cual nos hayamos registrado, se abrirá nuestra aplicación, si es la única que existe capaz de manejarlos o se mostrará una lista de aplicaciones disponibles.

Quizás en un primer momento pueda parecer algo que solo puede ser útil en ciertas condiciones muy específicas. Nada más lejos de la realidad, como vamos a ver a continuación.

Asociación de archivos

Al crear una asociación entre nuestra aplicación y un tipo de archivo, facilitamos que se lance automáticamente cuando el usuario quiera abrir un archivo en particular. El lanzamiento de nuestra aplicación puede producirse directamente desde el navegador, desde un correo electrónico, desde otra aplicación o incluso desde un archivo recibido por NFC o Bluetooth.

La asociación también determina el tipo de archivos que podemos leer desde el almacenamiento de la tarjeta SD extraíble, pero eso lo veremos en otro post.

Lo primero que necesitaremos para crear la asociación en nuestra aplicación a un tipo de archivo en concreto, son los logotipos a usar para mostrar el archivo en cuestión. En concreto necesitaremos tres imágenes: Una de 33x33 píxeles para mostrar si el archivo llega como adjunto en un correo electrónico. La segunda de 69x69 píxeles para mostrar en el hub de office. Por último necesitaremos otra de 176x176 píxeles para mostrar en la descarga del navegador.

image

Una vez que tengamos nuestros archivos de logotipo en los tres tamaños, podemos crear la asociación. Para ello tenemos que editar el archivo WMAppManifest.xml de nuestra aplicación, con el editor XML (Botón derecho Open With > XML (Text) Editor). Tendremos que añadir una sección llamada Extensions, justo debajo de Tokens. Dentro de esta sección crearemos un nodo FileTypeAssociation donde indicaremos el nombre, la TaskID asociada y el fragmento de Uri que se enviará a nuestra aplicación:

<Extensions>
  <FileTypeAssociation Name="geeksms file" TaskID="_default" NavUriFragment="fileToken=%s">
  </FileTypeAssociation>
</Extensions>

La propiedad Name se usará para mostrar el nombre del tipo de archivo. TaskID se refiere al TaskID de la sección Token, que define el arranque de nuestra aplicación, y que por defecto es _default. NavUriFragment indica el parámetro que se enviará a nuestra aplicación con el token del archivo, para poder abrirlo.

Una vez definidos estos tres valores, dentro de FileTypeAssociation incluiremos una sección Logos, donde indicaremos los tres logos que ya hemos incluido en nuestra aplicación:

<Extensions>
  <FileTypeAssociation Name="geeksms file" TaskID="_default" NavUriFragment="fileToken=%s">
    <Logos>
      <Logo Size="small" IsRelative="true">Assets/filetype_small.png</Logo>
      <Logo Size="medium" IsRelative="true">Assets/filetype_medium.png</Logo>
      <Logo Size="large" IsRelative="true">Assets/filetype_large.png</Logo>
    </Logos>
  </FileTypeAssociation>
</Extensions>

En cada logo indicamos su tamaño en la propiedad Size y en el contenido ponemos la ruta relativa, marcando a true la propiedad IsRelative. Debemos establecer los archivos como Content en su Build Action y Copy Always o Copy if newer en su propiedad Copy to Output Directory. A continuación solo nos queda indicar exactamente las extensiones de archivo que deseamos asociar, añadiendo dentro del nodo FileTypeAssociation la sección SupportedFileTypes:

<Extensions>
  <FileTypeAssociation Name="geeksms file" TaskID="_default" NavUriFragment="file=%s">
    <Logos>
      <Logo Size="small" IsRelative="true">Assets/filetype_small.png</Logo>
      <Logo Size="medium" IsRelative="true">Assets/filetype_medium.png</Logo>
      <Logo Size="large" IsRelative="true">Assets/filetype_large.png</Logo>
    </Logos>
    <SupportedFileTypes>
      <FileType ContentType="application/text">.geeksms</FileType>
    </SupportedFileTypes>
  </FileTypeAssociation>
</Extensions>

Dentro de la sección SupportedFileTypes incluimos un nodo FileType por cada extensión de archivo que deseemos asociar al tipo geeksms file. En este caso añadimos solo una, indicamos el tipo de contenido en su propiedad ContentType y la extensión, con el punto. Con esto hemos concluido la configuración de nuestro archivo asociado. Ahora tenemos que saber, de alguna forma, que nos están activando con un archivo asociado. Para ello usaremos una clase que herede de UriMapperBase. Siempre que nuestra aplicación se active desde un archivo asociado, recibiremos un enlace que contendrá el fragmento: “/FileTypeAssociation?” seguido del fragmento que hayamos indicado y el token del archivo. Lo que vamos a hacer es crear una clase nueva en nuestra aplicación, llamada FileUriMapper, que procese la Uri de lanzamiento de nuestra aplicación en busca del archivo. Si encuentra una asociación nos dirigirá a una página, de lo contrario iremos a otra:

public class FileUriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        string tempUri = uri.ToString();

        if (tempUri.Contains("/FileTypeAssociation?"))
        {
            int fileIDIndex = tempUri.IndexOf("fileToken=") + 10;
            string fileID = tempUri.Substring(fileIDIndex);

            string fileUri = string.Format(@"/SecondPage.xaml?file={0}", fileID);

            return new Uri(fileUri, UriKind.Relative);
        }
        else
            return uri;
    }
}

En esta clase, simplemente examinamos la Uri que nos llega. Si esta no contiene el fragmento “/FileTypeAssociation?”, devolvemos la misma Uri, pues se trata de un lanzamiento normal. Por el contrario, si lo contiene, buscamos el token, fileID, del archivo y se lo pasamos como parámetro a una página llamada SecondPage que se encargará de tratarlo. Ahora tenemos que modificar el método InitializePhoneApplication de la clase App para añadir nuestro FileUriMapper al Frame de la aplicación:

private void InitializePhoneApplication()
{
    if (phoneApplicationInitialized)
        return;

    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;
            
    RootFrame.UriMapper = new FileUriMapper();

    RootFrame.NavigationFailed += RootFrame_NavigationFailed;
    RootFrame.Navigated += CheckForResetNavigation;

    phoneApplicationInitialized = true;
}

Simplemente establecemos la propiedad UriMapper de RootFrame a una nueva instancia de nuestro mapeador. Así cuando se inicie la aplicación, la Uri de inicio pasará por nuestra clase, que podrá examinarla y devolver la Uri correcta en cada caso. En el método OnNavigatedTo de la página SecondPage.xaml, vamos a examinar la QueryString en busca del token de archivo.:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    string fileToken = NavigationContext.QueryString["file"];

    var file = await SharedStorageAccessManager.CopySharedFileAsync(ApplicationData.Current.LocalFolder, "File.geeksms", NameCollisionOption.ReplaceExisting, fileToken);
    var stream = await file.OpenReadAsync();

    IBuffer buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size);
    await stream.ReadAsync(buffer, (uint)stream.Size, InputStreamOptions.None);
    DataReader reader = DataReader.FromBuffer(buffer);
    textContent.Text = reader.ReadString(buffer.Length);
}

Con este token, podemos usar el método CopySharedFileAsync de la clase SharedStorageAccessManager. Este método nos devuelve una instancia de IStorageFile, con la que podremos acceder al stream del archivo, ya copiado en el espacio de nuestra aplicación. Con esto solo nos queda leer su contenido usando el método ReadAsync y un DataReader con el método ReadString para finalmente obtener el contenido en formato de texto.

image

Asociación de protocolos

De una forma muy parecida a como hemos realizado la asociación de archivos, podemos llevar a cabo una asociación de protocolos. ¿A qué nos referimos exactamente con un protocolo? Por regla general, nos referimos a una URL, que podría ser algo como lo siguiente:

geeksms:showarticle?ArticleId=100

Podemos asociar nuestra aplicación al protocolo geeksms: y recibir el resto de parámetros para decidir en que página iniciar la aplicación o que datos cargar. El primer paso para llevar a cabo esto, es registrar el protocolo al que deseamos responder (o protocolos, pueden ser más de uno). Lo realizaremos en el archivo WMAppManifest.xml, al igual que el registro de tipo de archivos:

<Extensions>
  <Protocol Name="geeksms" TaskID="_default" NavUriFragment="encodedLaunchUri=%s"/>
</Extensions>

Creamos una sección Extensions, si no existe ya, y dentro añadimos un nodo Protocol. El nombre debe ser único. Los otros dos parámetros son fijos, la tarea asociada (_default es la que abre nuestra aplicación) y el fragmento de navegación. Este fragmento debe ser siempre el mostrado en este ejemplo, dentro llegará la URL enviada a nuestra aplicación. Para poder saber cuando se está activando la aplicación desde un protocolo, usaremos una clase UriMapper, igual que con la asociación de archivos:

public class ProtocolUriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        string tempUri = HttpUtility.UrlDecode(uri.ToString());

        if (tempUri.Contains("geeksms:ShowArticle?ArticleId="))
        {
            string navUri = string.Format(@"/Views/SecondPage.xaml?protocol={0}", HttpUtility.UrlEncode(tempUri));
            return new Uri(navUri, UriKind.Relative);
        }
        return uri;
    }
}

En el método OnNavigatedTo de nuestra página SecondPage, obtendremos el parámetro protocol que enviamos para mostrarlo en pantalla:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    protocolTextBlock.Text = NavigationContext.QueryString["protocol"];
}

¿Como podemos probar que está funcionando? La activación por protocolo solo puede realizarse desde otra aplicación. Para ello haremos uso de otra novedad de Windows Phone 8, la clase Launcher, del namespace Windows.System.

Bonus: LaunchUriAsync y LaunchFileAsync

Otra de las novedades de Windows Phone 8 es la clase Launcher, del namespace Windows.System. Esta clase contiene dos métodos: LaunchUriAsync y LaunchFileAsync. Estos nos permiten lanzar de una forma sencilla una URL o un archivo.

LaunchUriAsync nos permite lanzar una URL y el sistema se encargará de buscar una aplicación asociada. Por ejemplo, podemos usar una dirección de un sitio web, de la siguiente forma:

await Windows.System.Launcher.LaunchUriAsync(new Uri("http://www.geeks.ms"));

Que dará como resultado la apertura de Internet Explorer. Pero también podremos lanzar una URL con un protocolo propio, lo que ejecutará nuestra aplicación:

await Windows.System.Launcher.LaunchUriAsync(new Uri("geeksms:ShowArticle?ArticleId=100"));

LaunchFileAsync nos permite realizar la misma operación, pero con un archivo. Pasando como parámetro la instancia del archivo contenida en un IStorageFolder. Por ejemplo, con un archivo que distribuyamos con la aplicación:

var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("file.geeksms");
await Windows.System.Launcher.LaunchFileAsync(file);

Conclusión

Y esto es todo por hoy. Hemos visto como asociar nuestra aplicación a tipos de archivos y protocolos y como lanzar desde nuestra aplicación, otras aplicaciones usando archivos y URLs. Como siempre, a continuación tenéis tres proyectos con los ejemplos que hemos desarrollado en el post para que juguéis con ellos.

Un saludo y Happy Coding!

Más artículos Página siguiente >