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!

[Libro] Desarrollo en Windows 8 y Windows Phone 8 con XAML y C#

Desarrollo-Windows-8-350

Hola a todos!

Hoy es un gran día para mi!!

Por fin está aquí el libro completo en el que, junto a Ibon Landa y Rafa Serna, he estado trabajando los últimos seis meses. Algunos ya pudisteis adquirir una preview del mismo. Pues ahora tenéis más para leer. La preview tenía unas 190 páginas y 5 capítulos,el definitivo, tiene 550 páginas, 12 capítulos y un apéndice. ¿Qué encontraremos que no estuviese en el anterior? Muchas cosas:

  • Buenas practicas en la implementación de MVVM: Inyección de dependencias, Servicios, Locator…
  • Pruebas unitarias para ambas plataformas
  • Ciclo de vida de aplicaciones
  • Acceso al sistema:
    • contratos
    • lanzadores
    • selectores
    • aplicaciones lenses
    • extensibilidad de búsqueda
    • uso de voz en wp8
    • acceso a la cámara…
  • Sensores:
    • GPS
    • nuevos mapas de Nokia
    • Acelerómetro
    • Brújula
    • Giroscopio…
  • NFC y Bluetooth
  • Tiles y notificaciones
  • Comunicaciones
  • Windows Azure

Y además de esto, todo lo que ya tenía el anterior libro de introducción. En total tendremos unas 360 páginas totalmente nuevas, casi el triple de páginas de la preview en total. Todo ello, aderezado con más de 120 ejemplos para ambas plataformas!

Espero que disfrutéis tanto leyéndolo como yo he disfrutado escribiéndolo! Podéis ver el índice completo, y comprarlo si os gusta, aquí

BONUS: Entre los compradores del libro, se sorteará un kit de desarrollo de Nokia, compuesto por un Lumia 820 y una cuenta Nokia Premium Developer Program: más información aquí

Un saludo y Happy Coding!

Windows Phone 8: Gestión de contactos

Hola a todos!

Hasta ahora, cualquier aplicación podía hacer uso del lanzador SaveContactTask para añadir un contacto al teléfono. Lo malo de este lanzador es que requiere intervención del usuario, por lo que está muy bien para guardar un contacto pero es inusable para guardar muchos más.

En Windows Phone 8 una de las novedades del sistema es el poder integrar aplicaciones VoIP con el sistema. Para ello estas aplicaciones pueden publicar la lista de contactos en el people hub. Pero esta característica no es única de las aplicaciones VoIP, cualquier aplicación puede crear su propia “Store” dentro del people hub y añadir contactos a ella de forma automática. Para ello haremos uso del namespace Windows.Phone.PersonalInformation.

Almacén de contactos

En este namespace Windows.Phone.PersonalInformation encontramos la clase ContactStore. Esta clase representa el almacén de contactos de nuestra aplicación. Si observamos como funciona el people hub en Windows Phone, veremos que todo contacto está asociado a un almacen: twitter, linkedin, facebook… Bien, con la clase ContactStore podremos acceder o crear el almacén de nuestra aplicación e indicar como se relaciona con el resto del sistema, usando el método CreateOrOpenAsync:

public async Task CreateContactsStoreForApplication()
{
    store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, 
                                                 ContactStoreApplicationAccessMode.ReadOnly);
}

Este método recibe dos parámetros: access y sharing. El primero, access, indica la forma en la que el sistema se relaciona con nuestro almacén. Podemos escoger en el enumerador ContactStoreSystemAccessMode entre dos valores: ReadOnly, el sistema solo puede leer los contactos y no se pueden editar o ReadWrite, el sistema puede modificar los contactos de esta ContactStore. El segundo parámetro, sharing, indica como se relacionan otras aplicaciones con nuestros contactos. Podemos escoger en el enumerador ContactStoreApplicationAccessMode entre dos valores: LimitedReadOnly, donde el resto de aplicaciones solo podrán obtener la imagen del contacto y su descripción, o ReadOnly, con la que otras aplicaciones podrán leer todas las propiedades de un contacto en nuestra Store.

En ningún caso indicamos el nombre del almacén, este es inferido directamente de nuestra aplicación.

Añadir contactos al almacén

Una vez que hemos creado o abierto nuestra ContactStore, podremos empezar a trabajar añadiendo contactos, consultando los ya creados o eliminando los que deseemos. Para crear o actualizar contactos usaremos la clase StoredContact que nos permitirá indicar los valores de las propiedades standard del contacto como pueden ser: nombre, email, etc…:

public async Task AddContactToStore(string name, string phone, string email, string alterEgo)
{
    StoredContact contact = new StoredContact(store);

    contact.RemoteId = Guid.NewGuid().ToString();
    contact.DisplayName = name;

    var contactProperties = await contact.GetPropertiesAsync();

    contactProperties[KnownContactProperties.Telephone] = phone;
    contactProperties[KnownContactProperties.Email] = email;

    ...
}

Siempre que creemos una nueva instancia de esta clase, tendremos que indicar una instancia de ContactStore válida, que usará internamente. Tras crear la instancia, existe una propiedad en especial muy importante: RemoteId. Esta identifica de forma única a nuestro contacto y no puede repetirse. En este caso le hemos indicado un GUID pero puedes usar cualquier cadena de texto siempre y cuando sea única. A continuación usamos el método GetPropertiesAsync, que nos devuelve un Dictionary<string, object> donde podremos indicar las propiedades del contacto, tales como teléfono, email, cumpleaños, compañía… para ello, usaremos el enumerado KnownContactProperties y así evitar el uso de “magic strings” que puedan producir errores. Pero, ¿Qué ocurre si nosotros tenemos campos que no coinciden con los del enumerado? ¿Estamos restringidos solo a usar estos y descartar el resto de información? No, para nada. Al igual que tenemos el método GetPropertiesAsync, disponemos de otro llamado GetExtendedPropertiesAsync este método nos permite obtener las propiedades “extras” que tiene un contacto, las no incluidas en la lista de propiedades conocidas. Se trata de un Dictionary<string, object> en el que podremos incluir cualquier propiedad que deseemos:

var extendedProperties = await contact.GetExtendedPropertiesAsync();

extendedProperties["AlterEgo"] = alterEgo;
extendedProperties["Home"] = "cave";

CONSEJO: Aunque de cara al ejemplo hemos usado “magic strings” para las claves, lo recomendable sería crear un enumerado, llamado por ejemplo ExtendedKnownProperties, que contenga todos los campos que necesitemos.

Una vez que hayamos terminado de añadir todos los campos extra que deseemos, es momento de guardar nuestro contacto. Para ello usaremos el método SaveAsync de la clase StoredContact, a continuación el código completo de nuestro método AddContactToStore:

public async Task AddContactToStore(string name, string phone, string email, string alterEgo)
{
    StoredContact contact = new StoredContact(store);

    contact.RemoteId = Guid.NewGuid().ToString();
    contact.DisplayName = name;

    var contactProperties = await contact.GetPropertiesAsync();

    contactProperties[KnownContactProperties.Telephone] = phone;
    contactProperties[KnownContactProperties.Email] = email;

    var extendedProperties = await contact.GetExtendedPropertiesAsync();

    extendedProperties["AlterEgo"] = alterEgo;
    extendedProperties["Home"] = "cave";


    await contact.SaveAsync();
}

En el ejemplo que he preparado, todo este código está aislado en un servicio llamado ContactsService que inyectamos en nuestra ViewModel, de forma que podamos invocarlo desde un comando:

public async void AddContactCommandExecute()
{
    this.IsBusy = true;

    await this.contactsService.CreateContactsStoreForApplication();
    await this.contactsService.AddContactToStore("Bruce Wayne", "555-123123", "bat@darkness.com", "Batman");

    this.IsBusy = false;
}

RECUERDA: Antes de poder ejecutar nuestra aplicación, tenemos que recordar añadir a las capacidades del manifiesto el uso de contactos (ID_CAP_CONTACTS) o fallará al intentar acceder a la ContactStore.

Si ejecutamos la aplicación ahora y presionamos el botón “Add a new contact to phone” se ejecutará el código que crea la store si no existe y añade el contacto. Después podemos ir al people hub y ver como aparece nuestra aplicación en la lista de proveedores de contactos y el contacto que hemos creado en la lista de contactos:

image

Consultar contactos del almacén

Bien, ya hemos visto como podemos crear nuestro propio almacén de contactos en el dispositivo y como añadir contactos al mismo. Ahora veamos como consultar esos contactos que hemos creado. Para ello usaremos el método CreateContactQuery de la clase ContactStore, a la que podemos pasarle una instancia de la clase ContactQueryOptions indicando la ordenación y los campos incluidos en la consulta:

ContactQueryOptions options = new ContactQueryOptions();

options.OrderBy = ContactQueryResultOrdering.FamilyNameGivenName;
options.DesiredFields.Add(KnownContactProperties.Email);
options.DesiredFields.Add(KnownContactProperties.GivenName);

ContactQueryResult query = store.CreateContactQuery(options);

En este caso, queremos que se ordenen los contactos por los apellidos y después por el nombre. También indicamos que deseamos incluir propiedades como el Email y el GivenName. a continuación ejecutamos el método CreateContactQuery con estas opciones y obtenemos nuestra consulta lista para ser ejecutada en la nueva instancia de ContactQueryResult. En este punto todavía no tenemos datos y no se ha realizado ninguna consulta, simplemente hemos preparado el “terreno”.

Ahora tenemos dos métodos en el objeto ContactQueryResult: GetContactsAsync, que nos devuelve una lista de contactos, y GetContactCountAsync, que nos indica el número de contactos en este almacén. Como podemos ver, no podemos realizar ningún tipo de filtrado en este punto. Llamamos al método GetContactsAsync y luego trabajamos con la lista de contactos devuelta, quedando nuestro método QueryContactByName de la siguiente forma:

public async Task<StoredContact> QueryContactByName(string name)
{
    ContactQueryOptions options = new ContactQueryOptions();

    options.OrderBy = ContactQueryResultOrdering.FamilyNameGivenName;
    options.DesiredFields.Add(KnownContactProperties.Email);
    options.DesiredFields.Add(KnownContactProperties.GivenName);

    ContactQueryResult query = store.CreateContactQuery(options);

    var contactList = await query.GetContactsAsync();

    return contactList.Where(c => c.DisplayName == name).First();
}

TRUCO: Si queremos que la interfaz de nuestro servicio (IContactsService) y nuestra ViewModel sean compatibles con Windows Phone 7 o Windows 8, es recomendable crearnos una clase que contenga los campos del contacto que necesitemos (dto o poco), en vez de devolver un StoredContact, así no tendremos problemas a la hora de implementar este servicio en otras plataformas.

Eliminar contactos del almacén

Ahora que sabemos como consultar un contacto y leerlo desde el almacén del dispositivo, veamos como eliminarlo. Esto es realmente sencillo, además nos apoyaremos en el método QueryContactByName que hemos creado anteriormente. Para eliminar un contacto usaremos el método DeleteContactAsync de la instancia de ContactStore que tenemos activa. A este método necesitamos indicarle el RemoteId del contacto, por lo que primero tendremos que buscar el contacto, si no lo tenemos ya:

public async Task DeleteContact(string name)
{
    var contactToDelete = await QueryContactByName(name);

    await store.DeleteContactAsync(contactToDelete.RemoteId);
}

¿Sencillo verdad? Si revisamos el código que hemos incluido en este artículo, veremos que no hay ninguna parte especialmente complicada. Clases con nombres muy descriptivos, métodos sencillos y directos. Creo que uno de los grandes éxitos de la gestión de contactos, y del API de Windows Phone 8 en general, es la sencillez de su uso.

Conclusión

Bueno, llegamos al fin. Esta es una de las características nuevas de Windows Phone 8 que habíamos pedido durante mucho tiempo los desarrolladores. Ahora es momento de demostrar las cosas increíbles que podemos hacer con los contactos! Como siempre, a continuación os dejo el código de ejemplo que hemos visto, perfectamente funcionando en un proyecto de Windows Phone 8, con MVVM, con Autofac, Comandos y todo bien comentado y en su sitio!!.

Un saludo y Happy Coding!


Geocodificación y Geocodificación Inversa en Windows Phone 8

Hola a todos!

En el último artículo hablamos sobre el nuevo control de mapas de Windows Phone 8 y el cálculo y dibujado de rutas e indicaciones. En este artículo vamos a ir un paso más allá. Usaremos el namespace Microsoft.Phone.Maps.Services para acceder a los servicios de geo codificación. Estos nos permitirán “traducir” coordenadas geográficas en direcciones, geo codificación inversa, y direcciones en coordenadas geográficas, geo codificación.

GeocodeQuery

La clase GeocodeQuery nos permite introducir un término de búsqueda y consultar a los servicios cartográficos de Nokia para obtener su representación en coordenadas geográficas. Su uso es muy parecido al RouteQuery que vimos en el artículo anterior. En este caso indicaremos la propiedad SearchTerm con la cadena de texto que deseamos buscar, manejaremos el evento QueryCompleted y llamaremos al método QueryAsync:

GeocodeQuery query = new GeocodeQuery()
{
    GeoCoordinate = new GeoCoordinate(),
    SearchTerm = "Calle Fontecha y Salazar 1, Bilbao"
};

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

En el manejador del evento QueryCompleted recibiremos como resultado una lista del tipo MapLocation. Recibimos una lista porque puede que el resultado del término de búsqueda que hemos introducido devuelva más de un resultado:

void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
    if (e.Error == null)
    {
        Results = new ObservableCollection<MapLocation>(e.Result);
    }
}

Cada objeto MapLocation nos ofrece dos propiedades que nos interesan:

  • GeoCoordinate: Contiene la información geográfica de la coincidencia: Altura, Latitud, Longitud.
  • Information. Contiene información adicional sobre el termino buscado. Principalmente encontraremos información sobre la dirección: Código postal, barrio, país, código ISO de país, ciudad, población…

El resultado de una búsqueda de este tipo podría ser parecido al siguiente (click en la imagen para ampliar):

image

En algunos casos, también obtendremos los valores de la propiedad BoundingBox de MapLocation. Esta propiedad nos indica el centro en coordenadas geográficas y los puntos cardinales como un LocationRectangle con el área que engloba el punto que se nos ha devuelto.

ReverseGeocodeQuery

Ahora que ya hemos visto como obtener una localización geográfica a partir de un término de búsqueda, veamos como obtener una dirección a partir de un punto geográfico. Esta técnica es la que conocemos como geo codificación inversa y nos apoyaremos en la clase ReverseGeocodeQuery para llevar a cabo el trabajo.

Su uso es idéntico a GeocodeQuery, la única diferencia la podemos encontrar en las propiedades que establecemos. Mientras que GeocodeQuery recibe un término de búsqueda y nos devuelve las coordenadas geográficas, ReverseGeocodeQuery recibe coordenadas geográficas y nos devolverá direcciones:

ReverseGeocodeQuery query = new ReverseGeocodeQuery()
{
    GeoCoordinate = new GeoCoordinate(43.26614, -2.92451)
};

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

El evento QueryCompleted nos devolverá una lista de MapLocation de la cual podremos obtener toda la información necesaria como la calle, número, código postal… como podemos ver a continuación (click en la imagen para ampliar):

image

MapsSettings

Para concluir este vistazo a los nuevos mapas de Windows Phone 8, vamos a hablar de la clase MapSettings. Esta clase se encuentra en el namespace Microsoft.Phone.Maps y expone dos propiedades: IsMapsDisabled y ApplicationContext.

IsMapsDisabled nos informa si los servicios de mapas están desactivados en el dispositivo.  Estoy investigando que significa esto exactamente, bajo que condiciones pueden estar desactivados y pondré una entrada con las respuestas que obtenga para clarificar el uso de esta propiedad.

ApplicationContext nos permite indicar propiedades que se aplicarán a todo el uso de los mapas o sus servicios. En este caso se trata del ApplicationId y el AuthenticationToken. Podremos obtener estos dos valores cuando vayamos a subir nuestra aplicación a la Windows Phone Store. Después de completar el primer paso con la información general de la aplicación. Se habilitarán las opciones, una de ellas es “Map Services” (click en la imagen para ampliar):

image

Dentro de esta opción podremos pulsar un enlace “Get Token” que nos dará un token y application id válido. Solo tendremos que incluirlo en nuestra aplicación y volver a compilarla para subir el paquete a la Store. Sin este paso, nuestra aplicación no se certificará.

Conclusión

Con lo que vimos en el artículo anterior sobre rutas y este articulo, tenemos toda la información necesaria para realizar cualquier tipo de aplicación que haga uso de los mapas, localización y direcciones. Desde luego se ha simplificado mucho su funcionamiento, ya no dependemos de otros servicios externos y todas las consultas, tanto de rutas como de geo codificación se usan de una manera muy similar.

Espero que disfrutéis y hagáis grandes aplicaciones con esta información. Un saludo y Happy Coding!

Nuevo control de Mapas y calculo de rutas en Windows Phone 8

Hola a todos!

Ya había pasado un tiempo sin escribir, es lo que tiene llegar a la fase final de un libro. De pronto el 5% del trabajo te ocupa el 100% del tiempo. Pero ya estamos aquí otra vez. Hoy quería hablar sobre más novedades y funcionalidades que podemos encontrar en los mapas de Windows Phone 8.

Soy un verdadero enamorado de los nuevos mapas incluidos con el sistema. Si has visto los ejemplos y slides de mis charlas o as acudido a alguna, habrás comprobado lo fácil que es crear mapas visualmente increíbles: con representación de edificios en tres dimensiones, controlando el ángulo de cámara, la inclinación del terreno, el tipo de información para peatones a mostrar… Pero lo realmente increíble de los mapas es que no son solo unos mapas sin más. Alrededor de ellos se encuentra toda una infraestructura que podemos usar en nuestras aplicaciones. En concreto hoy me quiero centrar en el cálculo de rutas y en la geo codificación inversa.

Microsoft.Phone.Maps.Services

Este namespace contiene todas las clases que usaremos para realizar consultas al sistema de mapas de Windows Phone 8. En particular nos centraremos en tres tipos: RouteQuery, que nos permitirá crear rutas con indicaciones, GeocodeQuery, que nos ayudará a resolver direcciones para obtener coordenadas geográficas y ReverseGeocodeQuery, que nos permitirá obtener direcciones a partir de coordenadas geográficas. En este primer artículo de la serie vamos a examinar en profundidad las rutas con RouteQuery.

RouteQuery

En Windows Phone 7.X la única forma, sencilla y mínimamente automatizada, de crear rutas A-B era usar el lanzador BingMapsDirectionsTask. Este lanzador recibía un punto inicial, un punto final y abría la aplicación de mapas del teléfono para mostrarlos. En Windows Phone 8 conservamos este lanzador, al que se ha añadido el MapsDirectionsTask, pero también tenemos la posibilidad de mostrar rutas dentro de un mapa en nuestra aplicación. Una ventaja de usar la clase RouteQuery es que podemos indicar más de dos puntos, inicial y final, y crear rutas con múltiples puntos intermedios.

En primer lugar tendremos que crear una nueva instancia de RouteQuery. Debemos indicar el modo de viaje mediante la propiedad TravelMode. Para ello usaremos el enumerado TravelMode, que nos ofrece dos opciones: Walking o Driving. A continuación también tendremos que indicar los puntos que compondrán al menos el inicio y final de la ruta mediante la propiedad Waypoints. Como mínimo debemos indicar dos puntos, pero podemos añadir n puntos intermedios que deseemos:

RouteQuery query = new RouteQuery()
{
    TravelMode = TravelMode.Walking,
    Waypoints = new List<GeoCoordinate>()
    {
        new GeoCoordinate(43.26614, -2.92451),
        new GeoCoordinate(43.26308, -2.94296)
    }
};

En el ejemplo anterior indicamos solo dos coordenadas y nuestro modo de viaje lo establecemos en “Walking”. Con esto ya hemos creado nuestro objeto RouteQuery. Ahora usaremos el método QueryAsync y manejaremos el evento QueryCompleted para obtener los resultados:

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

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
    if (e.Error == null)
    {
        if (GetRouteCompleted != null)
            GetRouteCompleted(e.Result, null);
    }
}

En el manejador recibimos los resultados en el parámetro QueryCompletedEventArgs<Route>, en la propiedad Result. Es importante que comprobemos que no se ha producido ningún error. A continuación, como nos encontramos en una ViewModel y tenemos que añadir la ruta al mapa, lanzamos un evento que hemos definido en nuestra ViewModel llamado GetRouteCompleted.

En el code behind de nuestra vista, nos suscribiremos al evento cuando naveguemos a la página y lo eliminaremos al navegar fuera de ella:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    vm.GetRouteCompleted += vm_GetRouteCompleted;
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    vm.GetRouteCompleted -= vm_GetRouteCompleted;
}

En el manejador vm_GetRouteCompleted solo tendremos que usar el método AddRoute del elemento Map para añadir la ruta y que este la dibuje. Adicionalmente, para situar la cámara en el primer punto del recorrido obtenemos las geometrías que componen la ruta. En particular la primera de ellas, que indica el punto de inicio:

private void vm_GetRouteCompleted(object sender, EventArgs e)
{
    var route = new MapRoute((Route)sender);
    GeoCoordinate firstPoint = route.Route.Geometry.First();

    mapElement.Center = firstPoint;
    mapElement.AddRoute(route);
}

Y con esto ya tenemos completado el trabajo para mostrar nuestra ruta en pantalla. Antes de continuar, tenemos que añadir al manifiesto de nuestra aplicación dos capacidades: ID_CAP_MAP e ID_CAP_LOCATION para que funcione correctamente. Una vez añadidas, ejecuta la aplicación y el resultado debería ser algo parecido a lo siguiente:

image

Ya tenemos nuestra ruta calculada y dibujada en el mapa. Además con la posibilidad de poner el mapa en 3D y los edificios queda muy bien. Quizás hechas de menos algo: Indicaciones. Ya que tenemos la ruta, estaría muy bien tener también las indicaciones. Pues las tenemos! Están contenidas en una propiedad llamada Legs dentro de nuestra ruta. En nuestra ViewModel vamos a exponer una colección de RouteManeuvers, el objeto que indica una maniobra. y la vamos a rellenar de la siguiente forma:

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
    if (e.Error == null)
    {
        Maneuvers = new ObservableCollection<RouteManeuver>(e.Result.Legs.SelectMany(l => l.Maneuvers));

        if (GetRouteCompleted != null)
            GetRouteCompleted(e.Result, null);
    }
}

¿Por qué usar un SelectMany? La propiedad Legs es una colección. Esta comprende las instrucciones para ir de un punto hasta el siguiente. Si al realizar la consulta de nuestra ruta hemos indicado tres puntos (A,B,C) tendremos dos Legs: uno con las indicaciones de A-B y otro de B-C. Por lo tanto usamos SelectMany para obtener todas las indicaciones de todos los segmentos de la ruta. A continuación solo nos queda añadir a nuestra interfaz de usuario una lista que muestre los puntos:

<ListBox Grid.Column="1" ItemsSource="{Binding Maneuvers}"
         Background="#66000000">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="0,0,2,5">
                <TextBlock Text="{Binding InstructionKind}"
                           Foreground="{StaticResource PhoneAccentBrush}">
                </TextBlock>
                <TextBlock Text="{Binding InstructionText}"
                           TextWrapping="Wrap">
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Principalmente cada objeto Maneuver nos expondrá dos propiedades: InstructionKind e InstructionText. InstructionKind indica el tipo de maniobra: TurnRight, TurnLeft, Start, End… mientras que InstructionText nos dará una descripción más detallada de la misma. Si ejecutamos ahora nuestra aplicación de ejemplo veremos el siguiente resultado:

image

Voila! de una forma rápida y sencilla tenemos nuestra ruta dibujada e indicaciones añadidas en la pantalla. Además del tipo de maniobra y la descripción, cada objeto Maneuver nos ofrece la longitud hasta el siguiente paso en metros y su posición geográfica, con lo que podríamos comprobar cuando nos acercamos al siguiente punto para usar la sintetización de voz de Windows Phone 8 y leer las instrucciones al usuario.

Conclusión

Esta vez, Microsoft ha hecho un gran trabajo con los mapas de Windows Phone 8. Con el apoyo de Nokia nos han dado la oportunidad de crear aplicaciones que hagan un uso intensivo de la cartografía y la localización. A continuación tenéis el ejemplo de este artículo para que le deis un repaso y juguéis con el aprovechando las vacaciones navideñas. Incluye uso de MVVM, Autofac, VMLocator, los mapas y las rutas!

Un saludo y Happy Coding!

Más artículos Página siguiente >