[Xamarin.iOS] Uso de DropBox Sync API con C#

DropBox ha hecho público, durante esta semana, la API para la sincronización del servicio de archivos en la “nube” para las plataformas iOS y Android.

 

En el caso del SDK de iOS, pese a que está íntegramente orientada a la plataforma iOS nativa, es posible integrarla en proyectos Xamarin.iOS (desde Visual Studio o Xamarin Studio) a través de los Binding Projects. Para los que no estan familiarizados con este tipo de proyectos, los Binding Projects son un tipo específico de proyecto que permite en enlace de las librerias nativas tanto de Apple como de terceros, como es el caso de DropBox o las impresoras Zebra, para que puedan ser utilizadas desde código administrado.

 

image

 

En otras palabras, básicamente lo que hace es enlazar la definición de las interfaces, clases, delegados, métodos, propiedades, etc.. de la librería nativa en base a un archivo header .h a su homólogo en C#:

    @interface Widget : NSObject {
        @property (nonatomic, readwrite, assign) CGPoint center;
    }

por ejemplo a:

[BaseType (typeof (NSObject))]
    interface Widget {
        [Export ("center")]
        PointF Center { get; set; }
    }

En definitiva, prácticamente todas las API de terceros (o almenos las mas importatens) tienen su correspondiente binding en Xaxmarin.iOS y la nueva DropBox Sync API no es menos. La podéis descargar desde aquí. Si clonáis el repositorio público monotouch-bindings vereis, además, todas las librerias  enlazadas con Xamarin.iOS, con ejemplos y documentación.

 

Para poder ejecutar el ejemplo y/o hacer uso de la libreria DropBoxAPISync.iOS teneis dos opciones. O crear un proyecto Xamarin.iOS Binding Project y añadir los archivos de datos que encontrareis en la carpeta binding del repositorio o bien ejecutar desde el Mac el siguiente comando:

$ cd /<<MiPath>>/monotouch-bindings/DropBoxSync/binding && make all

Esto generará la libreria DropBoxSync.iOS.dll y lo único que tendreis q hacer es referenciarla desde el ejemplo cargado en el Visual Studio. Ejecutar en el emulador o dispositivo físico y listos.

image

Anuncion oficial de Xamarin 2.0 ¡Ya está aquí!

image

Después de meses y meses de trabajo continuo hoy sale a la luz una de las obras de ingenieria mas excepcional y complejas de las que he tenido el placer de participar y que pretende ser un antes y un después para las apps multiplataforma de la mano de Xamarín, una –aún- empresa start-up con sede en Boston y San Francisco con una comunidad actual de más de 230.000 desarrolladores y un crecimiento del ultimo año de entorno al 300%.

Xamarin es, hoy por hoy, la unica plataforma que está 100% enfocada a hacer de C# el lenguaje de desarrollo por escelencia para la construcción de apliaciones nativas en entornos no Microsoft como demuestran los múltiples casos de éxito de empresas y aplicaciones del sector.

¿Qué es Xamarin 2.0?

Xamarin 2.0 es la nueva oleada de productos de la plataforma Xamarin para desarrollar aplicaciones multiplataforma con Visual C# de los que cabe destacar:

  • Nuevo IDE Xamarin Studio. Se trata de un IDE 100% multiplataform centrada especialmente en la experiencia del usuario siendo aún más intuitiva, con una interfaz de usuario extraordinaria y mucho más!

Xamarin Studio

  • ¡¡Soporte de Visual Studio para desarrollo de aplicaciones iOS!!! Si, habéis leído bien. Xamarin ha lanzado el soporte oficial para el desarrollo de aplicaciones iOS desde Visual Studio 2010 y 2012. Ahora los usuarios de Xamarin.iOS podrán escribir código fuente desde Visual Studio y debugar y ejectura la aplicación desde el propio VS, en el emulador o dispositivo físico conectado al Mac donde está instalado Xamarin.iOS. Se trata de un producto que ha venido siendo solicitiado por los clientes y con esta primera versión se ha conseguido enlazar el compilador de Xamarin.iOS instalado en un Mac con Visual Studio centrandose especialmente en la experiencia del desarrollador.

image

  • Xamarin Component Store. Se trata de una librería de pre-built components multiplataforma que está presente tanto para Visual Studio como Xamarin Studio y que pretende ser una store de código para aplicaciones multiplataforma con toda clase de componentes (gratuitos y de pago) tanto librerias como componentes gráficos.

image

  • Edición Started Edition ¡totalmente grauita! para tod@s aquellos que quieran conocer la potencia de la plataforma Xamarin, esta versión te permitirá desarrollar aplicaciones en cualquier plataforma con la única limitación de que el tamaño máximo de la aplicación es de 32k (en IL).

 

¿Y ahora qué?

¡Pues a ahora a descargarselo y trastearlo! Aprovechate de las ventajas de la edición Started Edition, prueba la versión completa con la trial de 30 dias en cualquier momento y comprueba por tí mismo la ventaja que tiene el conocer el tu lenguaje de programación favorito para adentrarte en plataformas como iOS con Xamarin.iOS o Android con Xamarin.Android. ¿Aún necesitas más? Pues mira aqui:

Más aún:

  • Registrate al evento online que ofreceran Nat Friedman y Miguel de Icaza el 5 de Marzo. Más info aqui.
  • ¿Quieres conocer a los Xamurais en persona? Ven al Evolve 2013 que tendrá lugar en Austin (TX) el 14-17 de Abril. Miguel de Icaza, Scott Hanselman, Nat Friedman y toda la plantilla de Xamarin estaremos por allí.

Si aún necesitas más info, no dudeis en contactarme. Prometo hablar largo y tendido sobre todo esto y más!

 

Ready in the back!!!

EVOLVE 2013, conferencia internacional de desarrollo móvil

image

Para los que queráis estar al día en cuanto a desarrollo de aplicaciones móviles con Visual C# se refiere, el próximo mes de Abril tendrá lugar en Austin (Texas, EEUU) 4 días repletos de sesiones (2 de Training y otras 2 más de conferencias) de la mano de los mayores expertos en los productos Xamarin.

 

Speakers de la talla de Miguel de Icaza, Scott Hanselman o Nat Friedman se daran cita durante los dias 14, 15, 16 y 17 en el lujoso Hotel Hilton Austin del que será sin duda la conferencia anual de referencia para los desarrolladores de plataformas Android y iOS con la plaraforma Xamarin. Diseño de aplicaciones móviles, experiencia de usuario, movilidad en el mundo empresarial, App marketing y monetización o incluso las mejores prácticas para soluciones Cross-Platform son sólo un ejemplo de  la temática que se tocará en Evolve 2013.

 

Tanto si estás interesado en adentrarte en el mundo de desarrollo de aplicaciones móviles con Visual C# para otras plataformas como Android o iOS o simplemente ya tienes experiencia y quieres aprender y estar al día de la mano de los mayores expertos, esta es tu gran oportunidad. Podéis registraros únicamente a la formación o a la conferencia o a ambas. Ah! y los 100 primeros inscritos tendran un descuento del 20%.

 

¡Nos vemos en Austin!

 

Más info | Evolve 2013

Nuevo curso de Mono for Android

Hace ya poco más de dos meses que me incorporé en Xamarin, Inc. y me ha sorprendido gratamente la gran acogida que ha tenido y está teniendo, especialmente en USA, dos de los productos estrellas de MONO, Mono for Android y MonoTouch.

Por experiencia propia la habilidad de conocer Visual C# es una gran ventaja a la hora de adentrarse a desarrollar para otras plataformas, en las que además de tener que lidiar con la idiosincrasia de la propia plataforma tienes que hacer lo propio con el entorno de desarrollo y más importante con el lenguaje de programación. Y es que aunque no soy un gran conocedor de java u objective-c la potencia que tiene Visual C# respecto a sus competidores es, en mi honesta opinión, importante.

Para todos aquellos que ahora tengáis la oportunidad o necesidad de adentrarse en el desarrollo de dispositivos Android con Visual C#, además de la aclamada librería de documentación que mantenemos en Xamarín, los amigos de CampusMVP han lanzado un curso que nada tiene que envidiarla con la ventaja de que además está en español.

Se trata de un curso de divididos en 10 módulos que tratan el ciclo de vida de desarrollo de aplicaciones Mono for Android, desde los cimientos de MONO y su adaptación a Android hasta el despliegue de aplicaciones en el Marketplace pasando por todas la características de la plataforma, Actividades, Notificaciones, Menús, controles estándares y customizados… vamos un curso que no os podéis perder escrito por un excelente profesional como lo es Rubén Rubio.

Ahora si, ya tenemos material didáctico en español y además de la mano de CampusMVP.

Más info | Sitio oficial del curso

Bye, bye… MVP

Como todo en esta vida, todo lo que empieza … acaba y ha llegado mi hora en el programa MVP. Probablemente el 2011 ha sido uno de los años más dificiles de mi vida y las comunidades han pasado a estar en un segundo plano, de hecho aún siguen estandolo pues tampoco las estoy disfrutando como hace 5 años, cuando empecé en el programa MVP.

Que me llevo…

Si tuviera que quedarme con algo del programa más allá “del titulo” y los viajes, sin dudarlo ni un solo segundo me quedo con toda la gente que he conocido y que espero poder mantener siempre y con los que he compartido programa desde el 2008, empezando por Alejandro Mezcua con el que compartí viaje a Seattle en mi primer MVP Summit en el 2008 y con el que tengo el privilegio de poder coger el teléfono y llamarlo que siempre está ahí. A Marino Posadas por las oportuniades que me ofreció en un momento muy delicado de mi vida. A Pep Lluis Baño, el padre de las comundiades y el “Papá” de muchos de nosotros –no lo digo por la edad, lo digo por la experiencia. A Hadi Hariri, el Che Guevara de la comunidad. Joder, ¡¡cuanto he aprendido de ti chaval!!! A mi muy mejor amigo, Roberto González, por las cervezas, la compañía y las risas, jamás olvidaré esas noches de Seattle. A Luis Fraile, porque siempre he acabado haciendo lo que él decía, y he de reconocer que siempre ha acertado. Al titán, Rodrigo Corral, por que pese a que haga regimen siempre será grande. A Unai Zorrila, por que es un cabrón y por que no hay ningúno como él, especialmente en lo profesional (de lo mejor sin duda). A Jorge Serrrano, creo que si alguien define el programa es él. A Lluis Franco del Hospitalete, mi otro yo, y el sabe porque lo digo (Cuanto te quiero chaval). A José Manuel Alarcón, como dice su paisano El Pazos, el so machi bum man, un currante como la copa del pino y el único que se atrevió a editarme el libro; gracias tio. A Juan Carlos González, uno de los MOSS men; tengo pendiente un viaje Cantabria, no lo olvides. A “El Ché El Bruno Capuano”, contigo aprendí q los argentinos sos grandes, tu el que más. A Marco Amoedo, mi compañero de habitación en mis últimos viajes con el he compartido más que habitación; no, no me refiero a eso, no seais mal pensados. A José Luis Latorre, con el que además de compartír patrón de nombre, comparto una buena amistad. Gracias por todo chaval y perdona si no he estado a la altura. A Toni Recio, al que le llamo con todo mi cariño y aprecio “El Follonero” y porque hace más de 3 años que le debo una comida (snif!!!!!) y pq ha sabido ver otra forma de ver las comunidades. A Marc Rubiño, al que veo a diario, por no darme más por saco con el Barça. Ah si, y por que si tengo dudas de ASP.NET, la daré la chapa a él. A Octavio Hernández, el único hombre que además del dominar el españo y el inglés, domina C#. A Julián Peris, porque es unico. De verdad, no hay nadie igual a él. Fue en su momento el alma matter de los MVP Summit. A Eduardo Ortega, el friki por excelencia. A David Nudelman, porque es unico en su especie porque, como su propio apellido indica, es español 100% .A los SolidQ boys: Eladio Rincón, Miguel de Egea, Salvador Ramos… la crem de la crem. A los chicos de Plain, desde Ibón Landa, “el Joseba” hasta Iván González. Gracias por cogerme el teléfono cuando he estado con problemas. Os debo una. A Juansa Llopis, “Palel” Pilar, Iñaki Ayúcar pq aun en la distancia nos mantenmos cercanos. Y como no, a la jefa, a Cristina González, por todo.

Ostia, me emocioné y no he podido evitar nombrar a tod@s los que me han venido a la cabeza y seguro que me olvido a alguien. Si es así no me lo tengais en cuenta; además nos volveremos a ver por ahí, un sarao sin El Alegre Bandolero, no es un sarao. :-)

Cuando nos volvamos a ver, que sea en los bares…

josemiguel

[ESTUDIOS] Los low-end Smartphones acapararán el mercado de telefonia móvil en el 2015

Psst: The latest mobile tech rumors could bring new offerings from Apple, Google, and more.

Según un reciente estudio llevado a cabo por IHS iSuppli Mobile Handset Market Tracker el mercado de los smartphones pueden llegar a acaparar el mercado del e-commerce a nivel mundial en el año 2015.

Las previsiones estiman que más de 1.000 millones de unidades serán vendidas durante el año 2015, doblando los actuales 478 millones de este año 2011. El incremento se situa, por lo tanto, en un 54.4% muy por encima del 32.5% de este mismo año.

Esta subida será principalmente promovida por la aparición de los Low Cost Smartphones, términales móviles de características limitadas en términos de capacidad y memoria en relación a los Smartphones de altas prestaciones. Dentro de este mismo estudio se prevee que el crecimiento porcentual de la gama Low Cost se sitúe por encima del 115% mientras que la tasa de crecimiento de los Smartphones de media y alta gama permanecerán en un 16.4%.

La bajada de precio para el consumidor así como la irrupción de las denomiadas economías emergentes de paises como China, India, la zona del sur este asiático así como Africa catapultaran este tipo de terminales móviles.

Un ejemplo claro de lo que se puede esperar lo materializa la empresa Samsung gracias a la aparición de terminales de bajo coste en mercados como el de China o Latino América con un incremento en ventas muy superior al de resto de competidores.

Por su parte, y con la anunciada bajada de requisitos de hardware anunciada en el MIX del 2011, Windows Phone 7.1 “Mango” pretende ser la lanzadera perfecta para otro monstruo de la telefonía móvil como es la compañía finlandesa Nokia.

Se abre, por tanto, un periodo en el que una nuevo concepción de Smartphone pretende tomar las riendas del apetitoso mercado de Internet en téminos de ventas, telefonía y como no, e-commerce.

Más info | Smartphones to Account for Majority of Cellphone Shipments by 2015

REACTIVE-ando información de la DGT en Windows Phone 7 (y II)

Main filtros detalles

 

Lo prometido es deuda y ya está publicada la aplicación –bastante simple- de ejemplo en el Marketplace de Windows Phone 7, después de sortejar algún contratiempo en el proceso de validación, especialmente con la gestión del estado de la aplicación.

Decir que la aplicación se llama ViV Trafico (Visor Incidencia Viarias), y en esta primera versión proporciona una opción de filtrado por Autonomia, Carretera y Población. Evidentemente se le puede sacar mucho más provecho pero mientras, el tiempo que tengo para dedicar no me da para más.

La aplicación la podéis descargar haciendo click en el siguiente logo.

icon_200

Por otro lado, también he compartido el código completo de la aplicación que está en el Marketplace, para vuestro interés. Está hospedado en http://vivtrafico.codeplex.com

 

Un saludo,

REACTIVE-ando información de la DGT en Windows Phone 7

Desde hace algún tiempo he estado mirando y estudiando las posibilidades de las Reactive Extensions -Rx-, las cuales se encuentran actualmente en los Labs de Microsoft. Con la aparicición de Windows Phone 7, tenemos a nuestra disposición parte de estas extensiones dentro de los ensamblados Microsoft.Phone.Reactive y System.Observable. De hecho existe una versión específica de Rx para WIndows Phone 7 además de las que vienen instaladas en la ROM por defecto pero cuya instalación no está incluida en la ROM.

El caso es que durante el fin de semana se me ocurrió la idea de obtener la información que la Dirección General de Tráfico expone a través de un feed en XML de forma que pueda ser consultada por una aplicación WP7. Recordaba algunos ejemplos, básicamente orientados a la API de Twitter, en los que mostraba toda la potencia de la Rx en operaciones asincronas y manejo de eventos.

Lo que inicialmente se convirtió en un ejercicio de refresco y puesta en práctica de las Rx finalmente se ha convertido en una aplicación que publicaré en el marketplace de WP7 en breve. Además, compartiré el código íntegro de dicha aplicación en codeplex. Anunciaré tales novedades en este mismo blog.

Información de la DGT

Antes de entrar en profundidad vamos a ver que es lo ofrece la API de la DGT. La url es : http://dgt.es/incidencias.xml y el resultado:

image

Es decir que un elemento o incidencia tiene el siguiente formato XML:

   1: <incidencia>
   2:     <tipo>METEOROLOGICA</tipo> 
   3:     <autonomia>ANDALUCIA</autonomia> 
   4:     <provincia>JAEN</provincia> 
   5:     <matricula>J</matricula> 
   6:     <causa>LLUVIA</causa> 
   7:     <poblacion>SANTA ELENA</poblacion> 
   8:     <fechahora_ini>2011-03-12 19:48</fechahora_ini> 
   9:     <nivel>VERDE</nivel> 
  10:     <carretera>A-4</carretera> 
  11:     <pk_inicial>245.0</pk_inicial> 
  12:     <pk_final>288.0</pk_final> 
  13:     <sentido>Ambos sentidos</sentido> 
  14:     <hacia>Ambos</hacia> 
  15: </incidencia>

 

Y la representación en C# es básicamente….

   1: public class Incidencia 
   2: {
   3:  
   4:     public Incidencia()
   5:     { }
   6:  
   7:     public string Tipo { get; set; }
   8:     public string Autonomia { get; set; }
   9:     public string Matricula { get; set; }
  10:     public string Causa { get; set; }
  11:     public string Poblacion { get; set; }
  12:     public string FechaHora { get; set; }
  13:     public string Nivel { get; set; }
  14:     public string Carretera { get; set; }
  15:     public string Incial { get; set; }
  16:     public string Final { get; set; }
  17:     public string Sentido { get; set; }
  18:     public string Hacia { get; set; }
  19: }

 

En realidad, en el proyecto final aparecen un par de campos más utilizados para notificar informaciones a la pantalla, hablaremos de ello más adelante.

 

REACTIVEando la solución

Decidí utilizar las Rx para, por un lado englobar la operación WebRequest y posterior deserialización del XML en una colección IObservable y por otro lado utilizar las extensiones de Rx para que de forma reactiva consulte la información expuesta en el API de la DGT cada 3 minutos y notifique los cambios a un subscritor que no es más que una colección ObservableCollection enlazada a una aplicación Windows Phone 7.

Vayamos por partes. Un esquema aproximado seria el siguiente:

 

Esquema

En primer lugar creamos la petición a la dirección URL donde se aloja el XML indicandole el método y el contenido.

   1: var webRequest = 
   2:     WebRequest.Create(new Uri("http://dgt.es/incidencias.xml"));
   3:  
   4: webRequest.Method = "POST";
   5: webRequest.ContentType = "text/XML";

 

A continuación realizamos la llamada asíncrona mediante BeginGetResponse y EndGetResponse y es aqui dónde entra en escena las Rx. Utilizando el método Observable.FromAsynPattern<T> podemos convertir la función Begin/End a una función asíncrona de la siguiente forma:

   1: var peticion = Observable.FromAsyncPattern<WebResponse>(
   2:                     webRequest.BeginGetResponse,
   3:                     webRequest.EndGetResponse);

De esta forma obtenemos un delegado del tipo System.Func<System.IObservable<System.Net.WebResponse>> representado por la variable implícita peticion, vamos, una función de salida con un iterador del tipo IObservable. Ahora es el turno de LINQ –de ahí q en ocasiones se conozcan las Rx como LINQ to Events. Lo q vamos a hacer es seleccionar la respuesta Web y retornarla en forma de IEnumerable<Incidencias>. La sentencia LINQ tendria el siguiente aspecto:

   1: IEnumerable<Incidencia> res = from elemento in peticion()
   2:     .Select(respuestaWeb =>
   3:     {
   4:         using (var rs = respuestaWeb.GetResponseStream())
   5:         {
   6:             return rs.Deserialize(respuestaWeb.ContentLength);
   7:         }
   8:     })
   9:     select elemento;

La responsabilidad de transformar el Stream que contiene todo el XML en un IEnumerable<Incidencias> recae sobre el método extensor –de Stream- Deserialize. Básicamente lo que hace es obtener del stream todo el XML y mediante LINQ to XML “deserializarlo” –no es exactamente una deserialización- en una lista generica del tipo  Incidendica tal y como se muestra a continuación:

   1: public static IEnumerable<Incidencia> Deserialize(this Stream objeto, long lenght)
   2: {
   3:     var readBuffer = new byte[lenght];
   4:  
   5:     IEnumerable<Incidencia> nuevasIncidencias = null;
   6:  
   7:     objeto.BeginRead(readBuffer, 0, readBuffer.Length, 
   8:                         readAr =>
   9:                             {
  10:                                 var read = objeto.EndRead(readAr);
  11:                                 var readText = Encoding.UTF8
  12:                                     .GetString(readBuffer, 0, readBuffer.Length);
  13:  
  14:                                 nuevasIncidencias = from elemento in XDocument.Parse(readText)
  15:                                                         .Descendants("raiz").Descendants("incidencia")
  16:                                                     select new Incidencia 
  17:                                                             {
  18:                                                                 Autonomia = elemento.Element(XName.Get("autonomia")).Value,
  19:                                                                 Carretera =
  20:                                                                     elemento.Element(XName.Get("carretera")).Value,
  21:                                                                 Causa = elemento.Element(XName.Get("causa")).Value,
  22:                                                                 FechaHora =
  23:                                                                     elemento.Element(XName.Get("fechahora_ini")).Value,
  24:                                                                 Final = elemento.Element(XName.Get("pk_final")).Value,
  25:                                                                 Hacia = elemento.Element(XName.Get("hacia")).Value,
  26:                                                                 Incial = elemento.Element(XName.Get("pk_inicial")).Value,
  27:                                                                 Matricula =
  28:                                                                     elemento.Element(XName.Get("matricula")).Value,
  29:                                                                 Nivel = elemento.Element(XName.Get("nivel")).Value,
  30:                                                                 Poblacion =
  31:                                                                     elemento.Element(XName.Get("poblacion")).Value,
  32:                                                                 Sentido = elemento.Element(XName.Get("sentido")).Value,
  33:                                                                 Tipo = elemento.Element(XName.Get("tipo")).Value
  34:                                                             };
  35:                             }, null);
  36:  
  37:     return nuevasIncidencias;
  38: }

 

Hasta aquí tenemos los puntos 1 y 2 del esquema resuelto. Todo esto deberíamos encapsularlo en un método de forma que podamos, posteriormente aplicarle algún tipo de comportamiento como por ejemplo exponerlo como Observable. Una forma de hacerlo seria apoyándonos en el método estático Defer de Observable tal y como se muestra a continuación.

   1: internal IObservable<IEnumerable<Incidencia>> PrepararPeticion()
   2: {
   3:     return Observable.Defer(() =>
   4:     {
   5:         var webRequest = 
   6:             WebRequest.Create(new Uri("http://dgt.es/incidencias.xml"));
   7:  
   8:         webRequest.Method = "POST";
   9:         webRequest.ContentType = "text/XML";
  10:  
  11:         var peticion = Observable.FromAsyncPattern<WebResponse>(
  12:             webRequest.BeginGetResponse,
  13:             webRequest.EndGetResponse);
  14:  
  15:         return from elemento in peticion()
  16:             .Select(respuestaWeb =>
  17:             {
  18:                 using (var rs = respuestaWeb.GetResponseStream())
  19:                 {
  20:                     return rs.Deserialize(respuestaWeb.ContentLength);
  21:                 }
  22:             })
  23:             select elemento;
  24:     });
  25: }

 

Para facilitarnos el trabajo vamos a encapsular el método en una clase llamada DgtContexto y dentro del constructor de éste vamos a exponer la colección obtenida del Request y representada por el método PrepararPeticion() de la siguiente forma:

   1: public DgtContexto(int frecuencia)
   2: {
   3:     PrepararPeticion()
   4:         .ObserveOnDispatcher()
   5:         .Subscribe(incidencias =>
   6:                         {
   7:                            //TODO
   8:                         });
   9: }

Dentro de la clase DgtContexto debemos exponer una propiedad ObservableCollection que será la que posteriormente servirá como origen de datos para la aplicación Windows Phone 7.

   1: public class DgtContexto: INotifyPropertyChanged
   2: {
   3:     public ObservableCollection<Incidencia> Incidencias{ get; set; }
   4:     public DateTime Hora { get; set; }
   5:  
   6:     public DgtContexto() {}
   7:  
   8:     internal IObservable<IEnumerable<Incidencia>> PrepararPeticion() {}
   9:  
  10:     public event PropertyChangedEventHandler PropertyChanged;
  11:  
  12: }  

Además creamos una propiedad Hora para conocer la fecha y hora de la información que nos hemos descargado y por útlimo implementamos, la interfaz INotifyPropertyChanged para propagar las modificaciones a la UI de la aplicación.

Ahora toca el turno a cómo relacionamos la lista IEnumerable<Incidencia> descargada del servidor de la DGT con la ObservableCollection de la clase DgtContexto. Esta operación se realizará en la subscripción a la colección retornada por el método PrepararPeticion(). Mediante expresiones lambda podemos obtener la acción onNext devuelta por el IObservable en forma de Action<IEnumerable<Incidencia>>, asignamos la lista iterativa IEnumerable<Incidencia> a la ObservableCollection y notificamos el cambio mediante la llamada a PropertyChanged de la interfaz INotifyPropertyChanged implementada anteriormente. Si, ya lo se, no es posible hacer un cast implícito de una colección IEnumerable<Incidencia> a una ObservableCollection<Incidencia> y para ello vamos a crear un método extensor que lo haga:

   1: public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> enumerable)
   2: {
   3:     var col = new ObservableCollection<T>();
   4:             
   5:     foreach (var cur in enumerable)
   6:     {
   7:         col.Add(cur);
   8:     } 
   9:             
  10:     return col;
  11: } 

Finalmente modificamos la propiedad Hora y notificamos y en definitiva el constructor con la suscripción completa quedaría de la siguiente forma:

   1: public DgtContexto()
   2: {
   3:     PrepararPeticion()
   4:         .ObserveOnDispatcher()
   5:         .Subscribe(incidencias =>
   6:                         {
   7:                             Incidencias = incidencias.ToObservableCollection();
   8:                             NotifyPropertyChanged("Incidencias");
   9:                             Hora = DateTime.Now;
  10:                             NotifyPropertyChanged("Hora");
  11:                         });
  12: }

Tenemos creada la infraestructura pero no estamos sacando provecho –del todo- a las Rx. Si queremos que la aplicación esté monitorizando las modificaciones de la información de la DGT podriamos crear un Timer o un Thread progamado para que lo consulte cada 3 minutos, por ejemplo. Otra opción seria utilizar las extensiones de Rx, más concretamente con los métodos Timer y Select, conjuntamente. El primero temporizará un periodo de tiempo representado por valores long desde 0 hasta TimeSpan.FromMinutes(frecuencia) donde el valor pasado por defecto desde el constructor vacío es 3. Luego proyectamos todos los valores devueltos por PrepararPeticion() desde el XML de la DGT y lo transformamos la collección IObservable más reciente mediante el método Switch(), con lo que el constructor quedará de la siguiente forma:

   1: public DgtContexto(int frecuencia)
   2: {
   3:  
   4:     Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(frecuencia))
   5:         .Select(_ => PrepararPeticion())
   6:         .Switch()
   7:         .ObserveOnDispatcher()
   8:         .Subscribe(incidencias =>
   9:                         {
  10:                             Incidencias = incidencias.ToObservableCollection();
  11:                             NotifyPropertyChanged("Incidencias");
  12:                             Hora = DateTime.Now;
  13:                             NotifyPropertyChanged("Hora");
  14:                             System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString() + " " +
  15:                                                                 Incidencias.Count.ToString());
  16:                             IsDataLoaded = true;
  17:                         });
  18: }

Podreis encontrar la clase completa a final del post.

 

Enlace con la UI

Para enlazar con la UI, basta con crear un proyecto Pivot o Panorama. En este blog indicaré como crear los datos de ejemplo y como queda el XAML del MainPage.xaml con dos campos enlazados. Básicamente el enlace de datos es identico a como se hace normalmente. Además de lo comentado anteriormente unicamente hay que modificar el archivo App.cs. Por partes, el App.cs quedará así:

   1: public partial class App : Application
   2: {
   3:     private static DgtContexto viewModel = null;
   4:  
   5:     /// <summary>
   6:     /// A static ViewModel used by the views to bind against.
   7:     /// </summary>
   8:     /// <returns>The MainViewModel object.</returns>
   9:     public static DgtContexto ViewModel
  10:     {
  11:         get
  12:         {
  13:             // Delay creation of the view model until necessary
  14:             if (viewModel == null)
  15:                 viewModel = new DgtContexto();
  16:  
  17:             return viewModel;
  18:         }
  19:     }
  20:  
  21: //el resto idéntico....
  22: //NOTE: clase incompleta
  23: }

NOTA: Unicamente he utilizado un par de campos en modo de test para comprobar el comportamiento de las Rx más que de la propia pantalla

El archivo xml de muestra:

   1: <DGT:DgtContexto 
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:DGT="clr-namespace:desarrolloMobile.DGT;assembly=desarrolloMobile.DGT"
   5:     Hora="12/12/2011 12:00:00">
   6:     
   7:     <DGT:DgtContexto.Incidencias>
   8:         <DGT:Incidencia Autonomia="CAT" Carretera="A2"/>
   9:         <DGT:Incidencia Autonomia="MAD" Carretera="A1"/>
  10:     </DGT:DgtContexto.Incidencias>
  11:     
  12: </DGT:DgtContexto>

Y el XAML de MainPage:

   1: <phone:PhoneApplicationPage
   2:     x:Class="desarrolloMobile.DGTViewer.MainPage"
   3:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   4:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   5:     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   6:     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   7:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   8:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   9:     mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" 
  10:     d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
  11:     FontFamily="{StaticResource PhoneFontFamilyNormal}"
  12:     FontSize="{StaticResource PhoneFontSizeNormal}"
  13:     Foreground="{StaticResource PhoneForegroundBrush}"
  14:     SupportedOrientations="Portrait"  Orientation="Portrait"
  15:     shell:SystemTray.IsVisible="True">
  16:  
  17:     <!--Data context is set to sample data above and LayoutRoot contains the root grid where all other page content is placed-->
  18:     <Grid x:Name="LayoutRoot" Background="Transparent">
  19:         <Grid.RowDefinitions>
  20:             <RowDefinition Height="Auto"/>
  21:             <RowDefinition Height="*"/>
  22:         </Grid.RowDefinitions>
  23:  
  24:         <!--TitlePanel contains the name of the application and page title-->
  25:         <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
  26:             <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
  27:             <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  28:         </StackPanel>
  29:  
  30:         <!--ContentPanel contains ListBox and ListBox ItemTemplate. Place additional content here-->
  31:         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  32:             <ListBox x:Name="MainListBox" Margin="8,-44,-20,44" ItemsSource="{Binding Incidencias}" SelectionChanged="MainListBox_SelectionChanged">
  33:                 <ListBox.ItemTemplate>
  34:                     <DataTemplate>
  35:                       <StackPanel Margin="0,0,0,17" Width="432">
  36:                           <TextBlock Text="{Binding Autonomia}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
  37:                           <TextBlock Text="{Binding Carretera}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
  38:                       </StackPanel>
  39:                     </DataTemplate>
  40:                 </ListBox.ItemTemplate>
  41:             </ListBox>
  42:             <TextBox Height="80" Margin="0,0,181,8" TextWrapping="Wrap" Text="{Binding Ticks, Mode=OneWay}" VerticalAlignment="Bottom" DataContext="{Binding Hora.TimeOfDay, Mode=OneWay}"/>
  43:         </Grid>
  44:     </Grid>
  45:  
  46: </phone:PhoneApplicationPage>
 
La captura de pantalla de la aplicación funcionando mostrando un par de campos en modo de test:
Main
 
NOTA: Imagen de la futura aplicación q colgaré en el MarketPlace a fecha de hoy y que implementa el código mostrado en este post.

 

Otros retoques

Por lo que he podido observar existen un promedio de más de 250 incidencias activas y ya q pretendo subir la aplicación al Marketplace he tratado q fuera algo más util. Es por ello que además vamos a filtrar las incidencias por carretera, autonomia y población.

La forma más sencilla de hacerlo es utilizando la propia información de la DGT, es decir hacer un SELECT DISTINCT de carreteras, autonomias y población de las 200 y pico incidencias y las muestro en un formulario de forma que el usuario seleccione el valor por el que filtrar. El código es el siguiente para cada una de los filtros:

   1: var context = (from incidencia in App.ViewModel.Incidencias
   2:                 select incidencia.Poblacion)
   3:                 .Distinct()
   4:                 .OrderBy(incidencia => incidencia);

 

Ahora, una vez mostrados por pantalla el usuario selecciona un valor, es decir, una carretera, una autonomia o una población. Sea cual sea lo que al final tenemos que hacer para mostrar los resultados filtrados es pasarle el valor de un predicado al método Where en el método Subscribe() del constructor DgtContexto de de la siguiente forma:

   1: public DgtContexto(int frecuencia, Func<Incidencia, bool> predicado)
   2: {
   3:     Predicado = predicado;
   4:  
   5:     Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(frecuencia))
   6:         .Select(_ => PrepararPeticion())
   7:         .Switch()
   8:         .ObserveOnDispatcher()
   9:         .Subscribe(incidencias =>
  10:                         {
  11:                             IncidenciasTotal = incidencias.ToObservableCollection();
  12:                             Incidencias = (incidencias
  13:                                 .Where(Predicado))
  14:                                 .ToObservableCollection();
  15:                             NotifyPropertyChanged("Incidencias");
  16:                             Hora = DateTime.Now;
  17:                             NotifyPropertyChanged("Hora");
  18:                             IsDataLoaded = true;
  19:                         });
  20: }

El tipo del predicado en los tres casos –esto es, filtro por autonomia, población o carretera- sera del tipo Func<Incidencia,bool>. Lo único q hacemos es crear una propiedad de ese tipo llamado Predicado en la clase DgtContexto que asignaremos cuando el usuario seleccione un valor del filtro:

   1: private void lstBoxAutonomia_SelectionChanged(object sender, SelectionChangedEventArgs e)
   2: {
   3:     ((TextBlock) lstBoxAutonomia.SelectedItem).FontSize -= 6;
   4:     App.ViewModel.Predicado = (incidencia => incidencia.Autonomia == ((TextBlock) this.lstBoxAutonomia.SelectedItem).Text);
   5:     NavigationService.GoBack();
   6: }
   7:  
   8: private void lstBoxPoblacion_SelectionChanged(object sender, SelectionChangedEventArgs e)
   9: {
  10:     ((TextBlock)lstBoxPoblacion.SelectedItem).FontSize -= 6;
  11:     App.ViewModel.Predicado = (incidencia => incidencia.Poblacion == ((TextBlock)this.lstBoxPoblacion.SelectedItem).Text);
  12:     NavigationService.GoBack();
  13: }
  14:  
  15: private void lstBoxCarretera_SelectionChanged(object sender, SelectionChangedEventArgs e)
  16: {
  17:     ((TextBlock)lstBoxCarretera.SelectedItem).FontSize -= 6;
  18:     App.ViewModel.Predicado = (incidencia => incidencia.Carretera == ((TextBlock)this.lstBoxCarretera.SelectedItem).Text);
  19:     NavigationService.GoBack();
  20: }

El resultado desde la interfaz gráfica es:

image

Y el resultado del filtro por COMUNIDAD_CANARIA:

image

Clase DgtContexto

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Collections.ObjectModel;
   4: using System.ComponentModel;
   5: using System.Linq;
   6: using System.Net;
   7: using Microsoft.Phone.Reactive;
   8:  
   9: namespace desarrolloMobile.DGT
  10: {
  11:     public class DgtContexto : INotifyPropertyChanged
  12:     {
  13:         public ObservableCollection<Incidencia> Incidencias { get; set; }
  14:         private ObservableCollection<Incidencia> IncidenciasTotal { get; set; }
  15:  
  16:         public DateTime Hora { get; set; }
  17:  
  18:         public DgtContexto()
  19:             : this(3, incidencia => true)
  20:         { }
  21:  
  22:         private Func<Incidencia, bool> _predicado;
  23:         public Func<Incidencia, bool> Predicado
  24:         {
  25:             get
  26:             {
  27:                 return _predicado;
  28:             }
  29:             set
  30:             {
  31:                 _predicado = value;
  32:                 if (Incidencias != null)
  33:                 {
  34:                     Incidencias = Incidencias
  35:                         .Where(Predicado)
  36:                         .ToObservableCollection();
  37:                     NotifyPropertyChanged("Incidencias");
  38:                     IsDataLoaded = true;
  39:                     NotifyPropertyChanged("Vacio");
  40:                 }
  41:             }
  42:         }
  43:  
  44:         public bool Vacio
  45:         {
  46:             get
  47:             {
  48:                 return !IsDataLoaded;
  49:             }
  50:         }
  51:  
  52:  
  53:         public DgtContexto(int frecuencia, Func<Incidencia, bool> predicado)
  54:         {
  55:             Predicado = predicado;
  56:  
  57:             Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(frecuencia))
  58:                 .Select(_ => PrepararPeticion())
  59:                 .Switch()
  60:                 .ObserveOnDispatcher()
  61:                 .Subscribe(incidencias =>
  62:                                {
  63:                                    IncidenciasTotal = incidencias.ToObservableCollection();
  64:                                    Incidencias = (incidencias
  65:                                        .Where(Predicado))
  66:                                        .ToObservableCollection();
  67:                                    NotifyPropertyChanged("Incidencias");
  68:                                    Hora = DateTime.Now;
  69:                                    NotifyPropertyChanged("Hora");
  70:                                    System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString() + " " +
  71:                                                                       Incidencias.Count.ToString());
  72:                                    IsDataLoaded = true;
  73:                                    NotifyPropertyChanged("Vacio");
  74:                                });
  75:         }
  76:  
  77:         public void Actualizar()
  78:         {
  79:             Incidencias = IncidenciasTotal;
  80:             NotifyPropertyChanged("Incidencias");
  81:             IsDataLoaded = false;
  82:             NotifyPropertyChanged("Vacio");
  83:         }
  84:  
  85:         internal IObservable<IEnumerable<Incidencia>> PrepararPeticion()
  86:         {
  87:             return Observable.Defer(() =>
  88:             {
  89:                 var webRequest =
  90:                     WebRequest.Create(new Uri("http://dgt.es/incidencias.xml"));
  91:  
  92:                 webRequest.Method = "POST";
  93:                 webRequest.ContentType = "text/XML";
  94:  
  95:                 var peticion = Observable.FromAsyncPattern<WebResponse>(
  96:                     webRequest.BeginGetResponse,
  97:                     webRequest.EndGetResponse);
  98:  
  99:                 return from elemento in peticion()
 100:                     .Select(respuestaWeb =>
 101:                     {
 102:                         using (var rs = respuestaWeb.GetResponseStream())
 103:                         {
 104:                             return rs.Deserialize(respuestaWeb.ContentLength);
 105:                         }
 106:                     })
 107:                        select elemento;
 108:             });
 109:         }
 110:  
 111:         public bool IsDataLoaded
 112:         {
 113:             get;
 114:             private set;
 115:         }
 116:  
 117:  
 118:         public event PropertyChangedEventHandler PropertyChanged;
 119:         private void NotifyPropertyChanged(String propertyName)
 120:         {
 121:             var handler = PropertyChanged;
 122:             if (null != handler)
 123:             {
 124:                 handler(this, new PropertyChangedEventArgs(propertyName));
 125:             }
 126:         }
 127:  
 128:         public void LoadData()
 129:         {
 130:             Incidencias = new ObservableCollection<Incidencia>
 131:                               {
 132:                                   new Incidencia
 133:                                       {
 134:                                           Autonomia = "---",
 135:                                           Carretera = "---"
 136:                                       }
 137:                               };
 138:         }
 139:     }
 140: }

[Windows Phone 7] Utilizar un certificado propio en Outlook

Hace relativamente poco acabo de recibir mi dispositivo Windows Phone 7. Tras tatar de conectarlo al Exchange de la empresa empezé a tener errores del estilo “Certificado no valido. Pongase en contacto con el administrador”.

Pese que a priori no es solución muy elegante lo resolví de la siguiente forma. En primer lugar abrir el Office Web Application (OWA) y miré que certificado es el que estaba utilizando –Si conoceis el nombre del certificado este paso lo podeis obviar- . En mi caso no es un certificado válido puesto que no está ortorgado por ninguna Autoridad Certificadora.

Localicé el certificado en el snap in o complemento de Certificados, en el MMC del sistema operativo. Esto es:

Ejecutar mmc:

image

agregar complemento o SnapIn:

image

seleccionar Certificados o Certificates de “Mi Cuenta de Usuario”

image

 

Una vez detectado el certificado(*) dentro de la carpeta “Entidades de certificación raíz de confianza” hacemos botón derecho y seleccionamos Exportar en el menú opciones. Lo exportamos a DER binario codificado X.509 (.CER) tal y como se muestra en la siguiente imagen:

image

(*) Si accedemos al Outlook a través del Office Web Access (OWA) el propio Explorador Web nos dirá que certificado está utilizando.

Seguidamente nos enviamos el archivo .cer del certicado adjunto por correo electronico a una cuenta que tengamos previamente configurada desde el dispositivo WP7. La cuestión es que al no tener ni acceso al sitema de archivos ni a los cetificados del dispositivo, el correo electronico es la unica forma de hacerlo llegar el archivo.cer referente  al certificado.

Cuando recibimos el correo electronico desde el dispositivo WP7 con el archivo.cer adjunto, lo descargamos y posteriormente lo ejecutamos. Nos pedirá confirmación explícitica de que realmente queremos instalar el certifiicado y ya está.

Ahora volvemos a tratar de sincronizar la cuenta de Outlook y si todo está bien, empezaremos a tener el correo sincronizado.

Espero que os sirva.

[OT] Foros MSDN sobre Windows Phone 7 en Español activos

Se han abierto recientemente el foro sobre Windows Phone 7 en Español, del cual soy administrador, en la siguiente dirección:

http://social.msdn.microsoft.com/Forums/es-es/windowsphone7/threads?prof=required

Os animo a que compartáis tanto dudas como experiencias sobre desarrollo en Windows Phone 7.

Nos vemos!!