[Evento SVQXDG] Programando para Apple Watch, Microsoft Band y Google Wear con Xamarin

El evento

En Sevilla, existen empresas y desarrolladores con un enorme talento
utilizando Xamarin para sus desarrollos de Apps móviles. Con este crisol
nació SVQXDG, o lo que es lo mismo, grupo de desarrolladores Xamarin de Sevilla. Es un grupo de usuario donde se busca tener un punto habitual de reunión para ayudar, compartir y aprender entre todos.

SVQXDG

SVQXDG

Tras un periodo veraniego de merecido descanso, el grupo vuelve con fuerza organizando un evento completo con múltiples sesiones abordando de fondo el desarrollo para  wearables.

La agenda

  • 18:30h -18:35h: Bienvenida. Recepción de asistentes, sesión introductoria donde repasar la agenda y otros detalles.
  • 18:35h – 19:20h: Desarrollo para el Apple Watch con Xamarin. De la mano de Josué Yeray tendremos una introducción al Watch Kit, estructura, diseño, controles y mucho más!
  • 19:20h – 19:25h: Descanso.
  • 19:25h – 20:10h: Posibilidades con Microsoft Band utilizando Xamarin. En esta sesión, con un servidor,
    veremos como crear Apps multiplataforma con Xamarin accediendo a la
    Microsoft Band, acceso a sensores, creación de Tiles, notificaciones,
    etc.
  • 20:10h – 20:15h: Descanso.
  • 20:15h – 21:00h: Introducción al desarrollo para Google Wear. De la mano de Marcos Cobeña tendremos una sesión donde repasaremos todo lo necesario para desarrollar para Google Wear.

El lugar

Tendrá lugar en el workINcompany. Tenéis la información exacta del lugar a continuación:

Calle Rioja 13 2A, Sevilla

Fecha

El evento tendrá lugar el próximo Miércoles, 16 de Septiembre de 18:30h a 21:00h. Tendremos tres sesiones de  45 minutos de duración cada una contando con 5 minutos de descanso entre sesiones.

¿Te apuntas?

Más información

[Windows 10] Trabajando con múltiples ventanas

Introducción

Hay ocasiones en las que contar con más de una ventana en la misma App
añade unas posibilidades increíbles mejorando exponencialmente el uso y
funcionalidad de la misma. Para centrarnos en lo que estamos hablando,
pongamos un ejemplo. El uso de PowerPoint proyectando una presentación.
La audiencia ve la presentación a pantalla completa mientras en otra
pantalla el ponente puede ver más información con más acciones (página
actual, siguiente, número de páginas, etc).

De igual forma, se nos pueden ocurrir cientos de escenarios donde
permitir tener varias ventanas, aprovechar múltiples pantallas, etc. En
este artículo vamos a aprender como permitir abrir más de una ventana de
la misma App y los detalles involucrados en el proceso.

NOTA: En Windows 8.1 ya teníamos la posibilidad de realizar esta acción. Ejemplo de App que hacía uso de la misma, la App de correo.

Múltiples ventanas en App de correo (Windows 8.1)

Múltiples ventanas en App de correo (Windows 8.1)

Múltiples ventanas

Conceptos básicos

Antes de “arrancar” vamos a repasar algunos conceptos
básicos. Cuando abrimos múltiples ventanas en la misma App, cada ventana
se ejecuta en un hilo y contexto diferente. No por ello serán bloques
aislados, podemos permitir la comunicación utilizando Dispatcher.BeginInvoke.

NOTA: Por supuesto también podemos utilizar datos almacenados en el almacenamiento aislado de la App, Roaming, etc.

Comenzamos!

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

Nuestro objetivo sera muy sencillo. Vamos a permitir crear una vista secundaria desde nuestra vista principal.

Comenzamos definiendo la vista principal:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Create Secondary View"/>
</StackPanel>

Muy simple, ¿cierto?. Tendremos un único botón que nos permitirá crear
la ventana secundaria. Para que el botón ejecute la acción necesitaremos
definir el comando necesario en la ViewModel:

private ICommand _createSecondaryViewCommand;
 
public ICommand CreateSecondaryViewCommand
{
     get { return _createSecondaryViewCommand = _createSecondaryViewCommand ?? new DelegateCommand(CreateSecondaryViewCommandExecute); }
}
 
private async void CreateSecondaryViewCommandExecute()
{
 
}

Bindeamos el comando con el botón:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Create Secondary View"
          Command="{Binding CreateSecondaryViewCommand}"/>
</StackPanel>

El resultado por ahora es:

Primera ventana

Primera ventana

Creando ventana secundaria

Para trabajar con ventanas utilizaremos para diferentes acciones
(switch, cerrar, etc) el identificador de la ventana. Podemos obtener el
identificador de una ventana utilizando el método GetApplicationViewIdForWindow disponible dentro de ApplicationView.

var _mainViewId = ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread());

La línea anterior nos permite obtener el identificador del CoreWindow específico correspondiente a nuestra vista y ventana principal.

Para abrir la nueva ventana lo primero que debemos hacer es… crearla:

var newView = CoreApplication.CreateNewView();

Utilizamos el método CreateNewView disponible en CoreApplication.
Esta línea crea un nuevo hilo y una nueva ventana en ese mismo hilo. La
ventana creada por supuesto no esta visible. La ventana visible es la
principal, creada en el arranque de la App. Es importante tener en
cuenta que, todos los eventos de activación se lanzarán en el hilo
principal, es decir, la ventana principal. Si se cierra la ventana
principal o su hilo de ejecución termina (sea cual sea el motivo) la App
se cerarrá.

Contenido de la ventana secundaria

Llegados a este punto, tenemos nuestra vista principal, pulsamos un
botón y creamos otro hilo donde creamos la ventana secundaria pero…¿que muestra?

Nuestro siguiente paso será indicar el contenido de la ventana secundaria:

var newView = CoreApplication.CreateNewView();
int newViewId = 0;
 
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
     var frame = new Frame();
     frame.Navigate(typeof(SecondaryView), null);
     Window.Current.Content = frame;
     Window.Current.Activate();
 
     newViewId = ApplicationView.GetForCurrentView().Id;
});

Navegamos a una nueva vista e importante, llamamos a la activación (necesario en Windows 10).

NOTA: Resaltar que utilizamos Dispatcher.RunAsync para comunicarnos con la vista, recordar que se usa un hilo diferente.

Mostrando la vista secundaria

Ya tenemos creada nuestra vista secundaria y hasta tiene contenido, nos falta mostrarla. Utilizaremos el método TryShowAsStandaloneAsync disponible en la clase ApplicationViewSwitcher:

var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                newViewId,
                ViewSizePreference.Default,
                ApplicationView.GetForCurrentView().Id,
                ViewSizePreference.Default);

Le pasamos el identificador de la nueva ventana y muestra la ventana secundaria junto a la ventana principal.

Definición de la vista secundaria

Hemos mostrado una vista secundaria pero nos falta ver su definición:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Switch to Main View"/>
     <Button
          Content="Switch to Main View and Hide"/>
</StackPanel>

Añadiremos dos botones. El primero de ellos nos pasará de la ventana
secundaria a la pantalla principal (cambiará la ventana activa)
manteniendo ambas ventanas mientras que el segundo botón realizará lo
mismo pero cerrando la ventana secundaria programáticamente.

Añadimos los comandos para los botones en la ViewModel:

private ICommand _switchViewCommand;
private ICommand _hideViewCommand;
 
public ICommand SwitchViewCommand
{
     get { return _switchViewCommand = _switchViewCommand ?? new DelegateCommand(SwitchViewCommandExecute); }
}
 
public ICommand HideViewCommand
{
     get { return _hideViewCommand = _hideViewCommand ?? new DelegateCommand(HideViewCommandExecute); }
}
 
private void SwitchViewCommandExecute()
{
 
}
 
private void HideViewCommandExecute()
{
 
}

Bindeamos los comandos correspondientes:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button
          Content="Switch to Main View"
          Command="{Binding SwitchViewCommand}"/>
     <Button
          Content="Switch to Main View and Hide"
          Command="{Binding HideViewCommand}"/>
</StackPanel>

El resultado tras abrir la ventana secundaria:

Ventana secundaria

Ventana secundaria

View Switching

Nos falta por ver un detalle muy importante, el intercambio y gestión entre ventanas.

para hacer el intercambio de una ventana a otra utilizaremos el método SwitchAsync disponible en la clase ApplicationViewSwitcher:

await ApplicationViewSwitcher.SwitchAsync(App.MainViewId);

A la llamada le pasamos como parámetro el identificador de la ventana
que deseamos que reemplace a la actual, en nuestro ejemplo, la ventana
principal.

De igual forma, además de poder hacer el intercambio de la ventana, podemos cerrar una secundaria:

await ApplicationViewSwitcher.SwitchAsync(App.MainViewId,
                ApplicationView.GetForCurrentView().Id,
                ApplicationViewSwitchingOptions.ConsolidateViews);

Aprovechamos otra sobrecarga del método SwitchAsync para
pasar además del identificador de la ventana a la que deseamos hacer el
switch, el identificador de la ventana que deseamos cerrar y el modo en
el que se realiza el intercambio entre ventanas. Para indicar el modo
utilizamos un parámetro de tipo ApplicationViewSwitchingOptions. Es una enumeración con los siguientes valores:

  • Default: Transición estándar entre ventanas.
  • SkipAnimation: Transición inmediata entre ventanas.
  • ConsolidateViews: Cierra la ventana, la quita del listado de usadas recientemente y vuelve a la ventana a la que se realiza el cambio.

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[Windows 10] Password Vault

Introducción

En muchas de las Apps que desarrollamos contamos con opciones relacionadas con la seguridad
donde debemos afrontar problemas como almacenar usuario y contraseña,
encriptar la información, gestionar múltiples dispositivos, etc.

Era un proceso “habitual” pero que requiere tener en cuentas
bastantes aspectos.  En este artículo vamos a ver lo sencillo que lo
tenemos utilizando las APIs Credential Locker disponibles en Windows.Security.Credentials.

NOTA: Estas APIs ya las teníamos disponibles desde Windows 8.1.

El almacén de credenciales

El almacén de credenciales nos permite almacenar y administrar de forma segura
contraseñas de usuarios de una aplicación o servicio específico de modo
que por un lado, los datos almacenados de una aplicación se transfieren
automáticamente a los otros dispositivos de confianza del usuario,
simplificando el proceso de autenticación tanto a los usuarios como a
nosotros, y por otro lado, no es posible desde una aplicación o servicio
acceder a los credenciales asociados con otra aplicación o servicio.

Password Vault

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

Nuestro objetivo sera muy sencillo. Nuestra aplicación de ejemplo
pedirá fuente, usuario y contraseña permitiéndo con tres sencillos
botones, añadir, recuperar y eliminar credenciales utilizando la API
Credential Locker.

Definiendo la interfaz de usuario

Comenzamos definiendo la interfaz de usuario:

<StackPanel Margin="12">
     <StackPanel
                Margin="0, 12">
                <TextBlock
                    Text="PasswordVault"
                    FontSize="32" />
     </StackPanel>
     <StackPanel Orientation="Vertical">
          <TextBox Header="Source"
                   PlaceholderText="https://facebook.com" />
          <TextBox Header="User"
                   PlaceholderText="user@mail.com" />
          <PasswordBox Header="Password"
                       PlaceholderText="1234abcd" />
     </StackPanel>
     <StackPanel Orientation="Horizontal"
                 HorizontalAlignment="Center"
                 Margin="0, 12">
          <Button Content="Save" />
          <Button Content="Read" Margin="12, 0" />
          <Button Content="Delete" />
     </StackPanel>
     <ScrollViewer>
          <TextBlock />
     </ScrollViewer>
</StackPanel>

Permitimos obtener la información de seguridad requerida y contamos con tres botones de acción. En la viewmodel correspondiente contaremos con propiedades para obtener la información escrita en cada una de las cajas de texto:

// Variables
private string _source;
private string _user;
private string _password;
private string _info;
  
public string Source
{
     get { return _source; }
     set { _source = value; }
}
  
public string User
{
     get { return _user; }
     set { _user = value; }
}
  
public string Password
{
     get { return _password; }
     set { _password = value; }
}
  
public string Info
{
     get { return _info; }
     set
     {
          _info = value;
          RaisePropertyChanged("Info");
     }
}

Y cada botón, ejecutará un comando en la viewmodel:

// Commands
private ICommand _saveCommand;
private ICommand _readCommand;
private ICommand _deleteCommand;
  
public ICommand SaveCommand
{
     get { return _saveCommand = _saveCommand ?? new DelegateCommand(SaveCommandDelegate); }
}
  
public ICommand ReadCommand
{
     get { return _readCommand = _readCommand ?? new DelegateCommand(ReadCommandDelegate); }
}
  
public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new DelegateCommand(DeleteCommandDelegate); }
}
  
public void SaveCommandDelegate()
{
  
}
  
public void ReadCommandDelegate()
{
  
}
  
public void DeleteCommandDelegate()
{
  
}

De modo que nuestra interfaz bindeada a las propiedades y comandos correspondientes quedara como podemos ver a continuación:

<StackPanel Margin="12">
     <StackPanel
                Margin="0, 12">
                <TextBlock
                    Text="PasswordVault"
                    FontSize="32" />
     </StackPanel>
     <StackPanel Orientation="Vertical">
          <TextBox Text="{Binding Source, Mode=TwoWay}"
                   Header="Source"
                   PlaceholderText="https://facebook.com" />
          <TextBox Text="{Binding User, Mode=TwoWay}"
                   Header="User"
                   PlaceholderText="user@mail.com" />
          <PasswordBox Password="{Binding Password, Mode=TwoWay}"
                       Header="Password"
                       PlaceholderText="1234abcd" />
     </StackPanel>
     <StackPanel Orientation="Horizontal"
                 HorizontalAlignment="Center"
                 Margin="0, 12">
          <Button Content="Save" Command="{Binding SaveCommand}" />
          <Button Content="Read" Margin="12, 0" Command="{Binding ReadCommand}" />
          <Button Content="Delete" Command="{Binding DeleteCommand}" />
     </StackPanel>
     <ScrollViewer>
          <TextBlock Text="{Binding Info}"/>
     </ScrollViewer>
</StackPanel>

Hasta aqui, la interfaz y estructura básica de nuestro ejemplo.

Nuestra interfaz

Nuestra interfaz

Gestión de credenciales

Para realizar la gestión de credenciales utilizaremos las APIs Credential Locker disponibles en Windows.Security.Credentials. Vamos a crear un servicio con la siguiente definición:

public interface IPasswordVaultService
{
     void Save(string resource, string userName, string password);
  
     PasswordCredential Read(string resource, string userName);
  
     IReadOnlyList<PasswordCredential> GetAll();
  
     void Delete(string resource, string userName);
}

NOTA: El servicio será inyectado por Ioc en nuestra viewmodel.

El servicio contará con cuatro métodos:

  • Save: Nos permitirá guardar credenciales.
  • Read: Nos permitirá recuperar un credencial concreto.
  • GetAll: Recupera todos los credenciales que tengamos almacenados en el Credential Locker.
  • Delete: Eliminará un credencial concreto almacenado previamente.

Nos centramos a continuación en la implementación de cada método. Comenzamos por el método Save:

public void Save(string resource, string userName, string password)
{
     PasswordVault vault = new PasswordVault();
     PasswordCredential cred = new PasswordCredential(resource, userName, password);
     vault.Add(cred);
}

Primero, obtenemos una referencia al Credential Locker usando on objeto de tipo PasswordVault definido en el namespace Windows.Security.Credentials. Continuamos creando un objeto de tipo PasswordCredential
que representará el credencial a almacenar con la referencia a nuestra
Aplicación o el tipo de Login utilizado además de los credenciales en
si, usuario y contraseña. Añadiremos el credencial creado al almacén de
credenciales utilizando el método PasswordVault.Add.

Continuamos con el método Read:

public PasswordCredential Read(string resource, string userName)
{
     PasswordVault vault = new PasswordVault();
  
     return vault.Retrieve(resource, userName);
}

Contamos con una gran variedad de opciones para recuperar
credenciales del almacén. En el código de la parte superior utilizamos
la forma más simple posible. Contando con el nombre de la App o tipo de
Login además del nombre de usuario, podemos recuperar la información
utilizando el método PasswordVault.Retrieve.

En el método GetAll utilizamos el método PasswordVault.RetrieveAll  para recuperar todos los credenciales almacenados en la Aplicación:

public IReadOnlyList<PasswordCredential> GetAll()
{
     PasswordVault vault = new PasswordVault();
  
     return vault.RetrieveAll();
}

Además de las dos formas utilizadas contamos con otras opciones para recuperar credenciales:

Por último, nos centramos en el método Delete, que como podemos imaginar se encargará de eliminar un credencial en concreto:

public void Delete(string resource, string userName)
{
     PasswordVault vault = new PasswordVault();
     PasswordCredential cred = vault.Retrieve(resource, userName);
     vault.Remove(cred);
}

De nuevo, es un proceso muy sencillo que podemos hacer con pocas
líneas. Accedemos de nuevo al almacén de credenciales mediante un objeto
de tipo PasswordVault y utilizamos el método PasswordVault.Remove  para eliminar el credencial almacenado pasado como parámetro.

Con nuestro servicio para gestionar el almacén de credenciales
preparado, solo nos falta definir la acción de cada comando. Al guardar
el credencial:

public void SaveCommandDelegate()
{
      if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User) || string.IsNullOrEmpty(Password))
      {
          Info += "The Source, the User and the Password are required." + "rn";
          return;
      }
 
      try
      {
          _passwordVaultService.Save(Source, User, Password);
          Info += string.Format("Credentials saved. Resource: {0}, User: {1}, Password: {2}",
                  Source, User, Password) + "rn";
      }
      catch(Exception ex)
      {
          Info += ex.Message + "rn";
      }
}
Credencial guardado

Credencial guardado

Para recuperar un credencial:

public void ReadCommandDelegate()
{
     if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User))
     {
          Info += "The Source and the User are required." + "rn";
          return;
     }
 
     try
     {
          var cred = _passwordVaultService.Read(Source, User);
          Info += string.Format("Data recovered successfully. Resource: {0}, User: {1}, Password: {2}",
                  cred.Resource, cred.UserName, cred.Password) + "rn";
      }
      catch (Exception ex)
      {
         Info += ex.Message + "rn";
      }
}
Recuperando credenciales

Recuperando credenciales

Utilizaremos tras validar la información, el método Read de nuestro servicio. Y por último, en el comando para eliminar credenciales:

public void DeleteCommandDelegate()
{
      if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User))
      {
          Info += "The Source and the User are requiered." + "rn";
          return;
      }
 
      try
      {
          _passwordVaultService.Delete(Source, User);
          Info += string.Format("Data successfully removed. Resource: {0}, User: {1}, Password: {2}",
                  Source, User, Password) + "rn";
      }
      catch (Exception ex)
      {
          Info += ex.Message + "rn";
      }
}

Eliminando credenciales

Eliminando credenciales

Utilizamos el método Delete del servicio.

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordar que podéis dejar en los comentarios cualquier tipo de sugerencia o pregunta.

Conclusiones

Bajo el namespace Windows.Security.Credentials contamos con una API llamada Credential Locker
que nos permite gestionar credenciales de usuario con suma facilidad.
La gran ventaja de utilizar la API es que nos almacena la información en
un almacén seguro, la información es encriptada al ser almacenada. Además, otra de las grandes ventajas de utilizar la API es el roaming de los credenciales entre dispositivos de confianza bajo la misma cuenta Microsoft.

A tener en cuenta

El almacén de credenciales esta pensado para facilitar la tarea de la
gestión de la seguridad en nuestras aplicaciones. No se recomienda su
uso para almacenar grandes cantidades de información. Contamos con otras
APIs válidas y más adecuadas para esta necesidad.

Más información

[Windows 10] Novedades XAML: x:DeferLoadStrategy

Introducción

Cuando desarrollamos aplicaciones móviles buscamos múltiples
objetivos, que sean atractivas visualmente, intuitivas, fluidas, ágiles,
etc. Habitualmente se le suele dar mucho peso a nivel funcional e
incluso en el aspecto visual pero en ocasiones, se deja la fluidez y agilidad de la App en un segundo plano realizando mejoras en fases finales y en ocasiones como mejoras futuras.

En este aspecto entran en juego herramientas fundamentales para el
análisis de rendimiento.  Consumo de CPU, memoria o red donde se suelen
apreciar métodos no asíncronos o pesados, la necesidad de cache para
reducir consumo de red, etc.

Con herramientas como el Visual Tree Explorer también se puede intuir y detectar casos de interfaz innecesariamente pesada donde los tiempos de carga
se elevan más de lo deseado. Hablamos de interfaces complejas. Por
ejemplo, vistas con diferentes paneles donde suele estar uno de ellos
visible y el resto ocultos o en un segundo plano a la espera de cierta
condición.

¿Qué podemos hacer en estos casos?

En este artículo vamos a desgranar el uso de x:DeferLoadStrategy que nos permitirá mejorar situaciones como las descritas anteriormente.

x:DeferLoadStrategy

x:DeferLoadStrategy nos permite retrasar la creación de un
elemento y sus elementos hijos lo que reduce los tiempos necesarios para
la creación de la UI y por lo tanto de carga. Sin embargo, nada en la
vida es gratis. A cambio, incrementamos levemente el consumo de memoria.

NOTA: Cada elemento que retrasamos en su inicialización con x:DeferloadStrategy añade 600 Bytes en el consumo de memoria.

Podemos deducir que a mayor cantidad de elementos que nos ahorremos
del árbol visual, en menor tiempo se realizara la inicialización de la
vista pero aumentando el consumo de memoria. Por lo tanto, el uso de la
etiqueta es recomendado aunque requiere un análisis mínimo.

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

Definimos nuestra interfaz:

<StackPanel Margin="12">
     <TextBlock
          Text="x:DefeLoadStrategy"
          FontWeight="Black"/>
     <TextBlock
          Text="x:DeferLoadStrategy nos permite retrasar la creación de un elemento y sus elementos hijos lo que reduce los tiempos necesarios para la creación de la UI y por
 
lo tanto de carga. Sin embargo, nada en la vida es gratis. A cambio, incrementamos levemente el consumo de memoria."
          TextWrapping="WrapWholeWords" />
     <Grid>
           <Image
                Stretch="UniformToFill"
                Source="ms-appx:///Assets/NinjaCat.jpg" />
     </Grid>
     <Button
          Content="Realize"
          Command="{Binding RealizeElementsCommand}"/>
</StackPanel>

Vamos a retrasar la creación del Grid que contiene la imagen para crearlo bajo nuestro propio interés al pulsar el botón. El uso es sencillo:

<Grid x:Name="DeferredPanel"  
      x:DeferLoadStrategy="Lazy">
      <Image
           Stretch="UniformToFill"
           Source="ms-appx:///Assets/NinjaCat.jpg" />
</Grid>

Utilizamos la etiqueta x:DeferLoadStrategy=”Lazy” en nuestro Grid. De esta forma indicamos que retrasamos la creación del panel y todo su contenido. Para utilizar la etiqueta debemos:

  • Definir un nombre con x:Name. Para iniciar posteriormente la incialización utilizaremos el nombre.
  • Podemos utilizarlo con cualquier elemento visual derivado de UIElement. No podemos utilizarlo con Page o UserControl.
  • no podremos utilizar con XAML XamlReader.Load.

Definimos en la ViewModel de la vista el comando a ejecutar cuando pulsemos el botón:

private ICommand _realizeElementsCommand;
 
public ICommand RealizeElementsCommand
{
     get { return _realizeElementsCommand = _realizeElementsCommand ?? new DelegateCommand(RealizeElementsCommandExecute); }
}
 
private void RealizeElementsCommandExecute()
{
 
}

Nos centramos ahora en el código que se ejecutará en la ejecución del
comando. Tenemos varias formas de crear el elemento que hemos retrasado:

var frame = (Frame)Window.Current.Content;
var page = (Page)frame.Content;
 
page.FindName("DeferredPanel");

La forma más habitual será utilizar el método FindName pasándole el nombre del elemento. Otras formas disponibles son:

  • Utilizar una propiedad del elemento retrasado en un Setter o StoryBoard de un VisualState.
  • Utilizar el elemento retrasado en una animación.
  • Utilizando el método GetTemplateChild pasándole el nombre del elemento.

Si ejecutamos la App veremos:

Antes de crear el Grid con imagen

Al pulsar el botón:

Tras crear Grid

El panel que retrasamos se crea. En este momento:

  • Se lanza el evento Loaded del panel.
  • Se evalúan los Bindings establecidos en el elemento.

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordar que podéis dejar en los comentarios cualquier tipo de sugerencia o pregunta.

Más información

[Tips and Tricks] Emulador Android de Visual Studio en VMWare Fusion

Introducción

Desde Visual Studio 2015 contamos con múltiples opciones para desarrollar Apps Android, C++, Cordova y C# con Xamarin. Sea cual sea el lenguaje utilizado, una herramienta necesaria será un emulador que nos permita arrancar y probar detalles de forma rápida y efectiva aumentando el ritmo de desarrollo.

NOTA: Recordar que el emulador nunca, repetimos,
nunca puede reemplazar a un dispositivo físico real siendo necesario
realizar pruebas en uno antes de lanzar la App en la tienda.

Emulador Android

Emulador Android

Con Visual Studio 2015 contamos con un emulador Android x86
compatible con Hyper-V integrado con Visual Studio aunque utilizable en
otros IDEs. Soporta múltiples versiones Android, resoluciones,
simulaciones de sensores, etc.

El problema

Realizando desarrollo multiplataforma, una opción interesante es un
mac potente que nos permita acceder a las herramientas de desarrollo iOS
y el resto de opciones de forma sencilla con virtualización utilizando
Parallels o VMWare Fusion.

Con la segunda de las opciones, VMWare Fusion, al intentar arrancar el emulador, obtenemos un error de arranque.

¿Qué ocurre?

El emulador no funciona al no tener soporte OpenGL en entornos virtualizados, como en el caso de VMWare Fusion.

La solución

Vamos a deshabilitar el soporte a OpenGL teniendo en cuenta que
podremos encontrarnos algunas características que no funcionen en este
modo.

Accedemos a la ruta:

C:Program Files (x86)Microsoft XDE10.0.10240.0SKUsAndroid

Abrimos el archivo xdesku.xml y borramos la línea:

GuestDisplayProvider="VsEmulator.OpenGLGuestDisplay"

Listo!.

Más información

[Windows 10] ”Project Westminster”, de la Web a App Universal

Introducción

Windows 10 ha llegado como la culminación en el viaje hacia la convergencia en el desarrollo entre plataformas Windows. Ahora hablamos de Apps Universales
escritas una única vez con un código común tanto para la lógica de
negocio como para la interfaz de usuario. Además, generamos un único
paquete que mantendrá una interfaz consistente y familiar para el
usuario pero adaptada a cada plataforma.

Windows 10

Windows 10

Podemos crear apps que funcionen en todo tipo de
dispositivos como teléfonos, tabletas, portátiles, dispositivos IoT,
Surface Hub e incluso HoloLens. Para ello tenemos las vías utilizadas
hasta este momento, es decir, u
tilizando C# y XAML (o VB, C++, etc).

Sin embargo, Windows 10 tiene como objetivo ser una plataforma
potente pero versátil y accesible para cualquier tipo de desarrollador
sin tener en cuenta su origen de partida.

Universal Windows Platform Bridges

Llegar a Windows es mucho más sencillo que nunca. Si tienes una web,
si tienes una App en iOS o Android e incluso si partes de una App Win32,
existen nuevas opciones destinadas a facilitar la llegada de esas Apps a
Windows de la forma más sencilla posible. Estamos hablando de los Windows Bridge toolkits.

Tenemos a nuestra disposición cuatro Bridges diferentes:

  • Project Westminster: Permite crear Apps Windows empaquetando una página web pudiendola publicar en la Store.
  • Project Islandwood: Windows Bridge para iOS
    (también conocido como proyecto Islandwood) es una herramienta que
    permite importar proyectos XCode para crear una App Windows 10
    reutilizando la mayor cantidad de código Objective C posible.
  • Project Astoria: Windows Bridge para Android
    (también conocido como proyecto Astoria) es una herramienta que permite
    comprobar la cantidad de código java a reutilizar, de modo que, nos
    permite crear App Windows 10 reutilizando código.
  • Project Centennial: Windows Bridge para App
    clásicas permite empaquetar y publicar Apps basadas en .NET y Win32 como
    Apps Windows Store. Además, permite el acceso a servicios o APIs UWP.
Universal Windows Platform Bridges

Universal Windows Platform Bridges

NOTA: Al momento de leer estas líneas solo
Project Westminster (Web) y Project Islandwood (iOS) están disponibles.
Project Astoria (Android) llegará en breve y para Project Centennial
(Win32) debemos esperar algo más, hasta final de año.

Project Westminster

El Bridge Windows para Apps web o también conocido como proyecto
Westminster permite crear y publicar una App Windows 10 utilizando una
web responsive para ello. Se reutiliza el código de la web para crear
una App disponible para teléfonos, tablets, PCs, Xbox, Raspberry Pi,
etc.

Entre las principales características tenemos:

  • Opción de reescalar y adaptar la App para ofrecer la mejor experiencia en cada familia de dispositivo.
  • Tenemos la posibilidad de acceso a APIs Windows desde Javascript.
    Por ejemplo, integrar comandos de voz con Cortana, añadir Live Tiles o
    notificaciones.
  • Podemos depurar con las herramientas de desarrollo de Microsoft Edge.
  • Publicar en la Store con acceso a analíticas, reviews, etc.
  • Monetizar añadiendo In-App Purchases.

Para poder crear nuestra App Windows 10 desde una web necesitamos unos requisitos mínimos:

  • Visual Studio 2015
  • Herramientas desarrollo Windows 10

De la Web a App!

1º Creamos App Universal Javascript

Comenzamos creando un nuevo proyecto de tipo Windows Universal Javascript:

Creamos nuevo proyecto

Creamos nuevo proyecto

Una vez creado tendremos la siguiente estructura en el proyecto:

Estructura inicial

Estructura inicial

2º Borramos todo excepto recursos y manifiesto

Procedemos a borrar todo el contenido exceptuando la carpeta images
con recursos y el archivo de manifiesto. La estructura del proyecto se
quedara:

Borramos lo necesario

Borramos lo necesario

3º Editamos el manifiesto

La página de inicio del archivo de manifiesto estara asignada a un archivo que acabamos de borrar:

Archivo de manifiesto

Archivo de manifiesto

Realizamos el cambio por la página web que vayamos a utilizar como App:

Página a utilizar

Página a utilizar

Además, añadimos una regla de contenido a nuestra URI para especificar la posibilidad de navegación y tipo de acceso:

URI de contenido

URI de contenido

4º Ejecutamos

Todo listo. Ejecutamos nuestra App:

Voila!

Voila!

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

¿Y ahora?

Ahora la parte más importante de todas, hacer de la App, una App
Windows de la máxima calidad. Podemos añadir integración con Cortana,
notificaciones, etc. Nos centraremos en opciones de esta índole en otro
artículo.

Más información

[Tips and Tricks] Xamarin Android Player en Windows 10

Introducción

Xamarin Android Player es un emulador Android
utilizando una máquina virtual x86 con aceleración por hardware y
OpenGL. Es significativamente más rápido que los emuladores Android
instalados por defecto.

Además, Xamarin Android Player esta disponible para Windows y OSX integrado tanto con Visual Studio como con Xamarin Studio.

El problema

Sin duda una opción potente a utilizar en nuestros desarrollos. sin embargo, tras actualizar a Windows 10 o instalándolo en Windows 10 obtenemos el siguiente error al intentar arrancar:

Could not configure host-only network. Please reboot the system…

Could not configure host-only network. Please reboot the system...

Could not configure host-only network. Please reboot the system…

Cada vez que intentamos arrancar el emulador se intenta crear un
nuevo adaptador de red, fallando una y otra vez. El reinicio de la
máquina no soluciona el problema. Si abrimos VirtualBox y nos dirimos a
las preferencias y red, veremos un adaptador por cada vez que intentamos
arrancar el emulador…

NOTA: XAP (Xamarin Android Player) no logra crear automáticamente el adaptador de red necesario.

La solución

Para poder arrancar el emulador debemos:

  • Desintalar Xamarin Android Player y VirtualBox por completo, incluidos datos relacionados con nuestro usuario.
  • Volvemos a instalar de nuevo Xamarin Android Player. Instalará VirtualBox.

NOTA: No arrancamos e instalamos aún emuladores, debemos realizar unos cambios antes de poder hacerlo.

  • Instalamos la última versión de VirtualBox (5.0.X)
  • Abrimos VirtualBox y accedemos a las preferencias de red, Redes solo-anfitrión:
Xamarin Android Player W10 03

Configuración adaptador red

  • Hacemos doble clic sobre el adaptador para editar la configuración e introducimos:
    • IPv4 Address: 10.71.34.1
    • IPv4 Network Mask: 255.255.255.0
  • Arrancamos!. Podremos instalar emuladores. Cada emulador debe arrancar sin problemas y con acceso a la red.
Xamarin Android Player en Windows 10

Xamarin Android Player en Windows 10

Más información

[Windows 10] Probando “Project Islandwood”, reutilizando código Objective-C en App Windows 10

Introducción

Windows 10 ha llegado como la culminación en el viaje hacia la convergencia en el desarrollo entre plataformas Windows. Ahora hablamos de Apps Universales
escritas una única vez con un código común tanto para la lógica de
negocio como para la interfaz de usuario. Además, generamos un único
paquete que mantendrá una interfaz consistente y familiar para el
usuario pero adaptada a cada plataforma.

Windows 10

Windows 10

Podemos crear apps que funcionen en todo tipo de
dispositivos como teléfonos, tabletas, portátiles, dispositivos IoT,
Surface Hub e incluso HoloLens. Para ello tenemos las vías utilizadas
hasta este momento, es decir, u
tilizando C# y XAML (o VB, C++, etc).

Sin embargo, Windows 10 tiene como objetivo ser una plataforma
potente pero versátil y accesible para cualquier tipo de desarrollador
sin tener en cuenta su origen de partida.

Universal Windows Platform Bridges

Llegar a Windows es mucho más sencillo que nunca. Si tienes una web,
si tienes una App en iOS o Android e incluso si partes de una App Win32,
existen nuevas opciones destinadas a facilitar la llegada de esas Apps a
Windows de la forma más sencilla posible. Estamos hablando de los Windows Bridge toolkits.

Tenemos a nuestra disposición cuatro Bridges diferentes:

  • Project Westminster: Permite crear Apps Windows empaquetando una página web pudiendola publicar en la Store.
  • Project Islandwood: Windows Bridge para iOS
    (también conocido como proyecto Islandwood) es una herramienta que
    permite importar proyectos XCode para crear una App Windows 10
    reutilizando la mayor cantidad de código Objective C posible.
  • Project Astoria: Windows Bridge para Android
    (también conocido como proyecto Astoria) es una herramienta que permite
    comprobar la cantidad de código java a reutilizar, de modo que, nos
    permite crear App Windows 10 reutilizando código.
  • Project Centennial: Windows Bridge para App
    clásicas permite empaquetar y publicar Apps basadas en .NET y Win32 como
    Apps Windows Store. Además, permite el acceso a servicios o APIs UWP.
Universal Windows Platform Bridges

Universal Windows Platform Bridges

NOTA: Al momento de leer estas líneas solo
Project Westminster (Web) y Project Islandwood (iOS) están disponibles.
Project Astoria (Android) llegará en breve y para Project Centennial
(Win32) debemos esperar algo más, hasta final de año.

Project Islandwood

Project Islandwood es el Bridge Windows para iOS. Permite importar proyectos existentes iOS para crear Apps Windows reutilizando la mayor cantidad de código Objective-C posible.

NOTA: En estos momentos no se soporta Swift.

Windows Bridge para iOS

Windows Bridge para iOS

Las bondades de la herramienta son:

  • Se soporta Objective-C tanto a nivel de compilación como Runtime.
  • Tenemos herramienta en línea de comandos para importar proyectos.
  • A nivel de editor (Visual Studio) contamos con opciones de coloreado, autocompletado, etc. Es decir, el lenguaje esta soportado.
  • Contamos con opciones de depuración (puntos de ruptura, etc), stacktraces, etc.
  • Opciones de configuración. La configuración del proyecto importado es compartida.
  • Podemos añadir características Windows a la App.

Crear Apps Windows reutilizando código Objective-C

Para utilizar el Bridge necesitamos:

  • Windows 10
  • Visual Studio 2015 (puede ser versión Community)
  • El SDK winobjc

Nuestro proyecto iOS

Llegados a este punto, tenemos claro nuestro objetivo, ¿cierto?.
Vamos a tomar una App iOS utilizando Objective-C y vamos a utilizar
WinObjc para crear una App Windows 10 reutilizando el código de la App
iOS.

Estaría genial entrar de lleno al uso, posibilidades y limitaciones del Bridge pero antes de eso necesitamos…una App iOS.

Vamos a crear una App simple, muy simple, podríamos decir que haremos
uno de los clásicos “Hola mundo”. Tendremos una caja de texto donde el
usuario pondrá su nombre de modo que al pulsar un botón se mostrará un
mensaje saludándole.

Abrimos XCode y creamos un nuevo proyecto iOS:

Nuevo proyecto

Nuevo proyecto

Seleccionamos la plantilla “Single View Application” y Objective-C como lenguaje a utilizar:

Configuración básica del proyecto

Configuración básica del proyecto

Proyecto creado. En el AppDelegate definimos opciones básicas de la ventana y establecemos el controlador a utilizar:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor lightGrayColor];
     
    MainViewController *viewController = [[MainViewController alloc] init];
    self.window.rootViewController = viewController;
     
    [self.window makeKeyAndVisible];
    return YES;
}

Añadiremos una caja de texto junto a un botón:

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Text field
    self.textField = [[UITextField alloc]
                              initWithFrame:CGRectMake(10.0f, 50.0f, 300.0f, 30.0f)];
    self.textField.delegate = self;
     
    self.textField.borderStyle = UITextBorderStyleRoundedRect;
     
    // Add the text field to the view
    [self.view addSubview:self.textField];
     
    // Button
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
     
    // Button's frame
    button.frame = CGRectMake(100.0f, 80.0f, 120.0f, 30.0f);
     
    // Action
    [button addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
     
    [button setTitle:@"Press Me!" forState:UIControlStateNormal];
     
    // Add the button to the view
    [self.view addSubview:button];
}

Utilizamos el evento UIControlEventTouchUpInside para mostrar una alerta:

- (void)buttonPressed {
    // Show AlertView
    UIAlertView *helloWinObjcAlert = [[UIAlertView alloc]
                                      initWithTitle:@"Hello, WinObjc!" message:[@"Hello " stringByAppendingString:self.textField.text] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
     
    [helloWinObjcAlert show];
}

En la alerta, mostramos un saludo junto al valor introducido en la caja de texto. Si ejecutamos nuestra App:

iOS App

iOS App

Introduciendo un nombre y pulsando el botón:

Mostrar saludo

Mostrar saludo

Simple, ¿verdad?. Cierto pero suficiente para probar el Bridge. Podéis descargar el ejemplo completo realizado a continuación:

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Creando la App Windows 10

Con la App iOS a migrar esperándonos, ahora si, nos lanzamos a
utilizar el Bridge. Comenzamos clonando o descargando como zip el SDK a
nuestra máquina de desarrollo:

WinObjc en GitHub

WinObjc en GitHub

NOTA: Podemos tener el SDK en cualquier ruta de nuestro equipo. La única limitación es no tener espacios en la ruta.

Antes de utilizar la herramienta para importar nuestro proyecto
XCode, es recomendable echar un vistazo a los ejemplos incluidos en la
carpeta Samples. Sobretodo, es muy interesante para ver posibilidades ej
ejemplo WOCCatalog:

WOCCatalog

WOCCatalog

Utilizando vsimporter

Vamos a importar nuestro proyecto XCode. Tras descargar el SDK precompilado de este enlace, y descomprimirlo en una ruta:

c:winobjc

Creamos una carpeta “Import” donde copiamos nuestro proyecto XCode. A continuación, abrimos la línea de comandos.

Accedemos a la carpeta donde tenemos nuestro proyecto:

c:winobjcImportHelloIslandwood

Ejecutamos la herramienta vsimporter:

c:winobjcImportHelloIslandwood> ....binvsimporter.exe

NOTA: Es recomendable utilizar rutas relativas con la herramienta. Rutas absolutas pueden ocasionar problemas.

Esta herramienta comenzará a migrar nuestor proyecto creando una solución Visual Studio.

Una vez terminada, si accedemos a la carpeta.

Proyecto creado por vsimporter

Proyecto creado por vsimporter

Voila!.

Otras opciones

Como hemos visto, vsimporter es una herramienta de línea de comandos. Cuenta con importantes parámetros como:

  • -i: Habilita el modo interactivo donde se permite seleccionar la configuración específica del proyecto XCode.
  • -format: La opción más interesante. Por defecto, la herramienta
    convierte el proyecto XCode a Windows 10. Con esta opción podemos elegir
    si migrar a Windows 10 (winstore10) o Windows 8.1 (winstore8.1 o winphone8.1).
  • -help: No necesita mucha explicación, muestra un listado de opciones para ayudar en el uso de la herramienta.

Un vistazo al proyecto creado

Abrimos el proyecto creado:

Estructura del proyecto

Estructura del proyecto

Tenemos una solución con dos proyectos, el principal y otro que
contiene los archivos .h, las cabeceras. Cetrándonos en el proyecto
principal, el código principal de la App se encuentra dentro de una
carpeta con el nombre de la App, en nuestro ejemplo HelloIslandwood. El código sigue siendo el mismo que en el proyecto Objective-C que analizamos previamente.

NOTA: Importante resaltar que tenemos reconocimiento de tipo de archivos, coloreado, etc. desde Visual Studio.

Probando la App

Si ejecutamos nuestro proyecto:

Nuestra App iOS en Windows 10!

Nuestra App iOS en Windows 10!

Tras poner el nombre en la caja de texto y pulsar el botón:

Funciona!

Funciona!

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

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Limitaciones

No es oro todo lo que reluce…

La herramienta esta bajo desarrollo, tenemos disponible la primera versión pública y no soporta muchas opciones como:

  • Storyboards. Quizás una de las limitaciones más
    importantes debido al impacto que ocasiona en infinidad de posibles
    apps. Tras múltiples pruebas realizadas, la migración se realiza, crea
    el proyecto Visual Studio pero, no veremos nada (pantalla en negro) al
    ejecutar la App.
  • Sólo x86. No se soporta aún ARM. Otra limitación importante.
  • MapKit
  • AssetsLibrary
  • Ads
  • Etc

Como podemos ver, aun faltan opciones importantes que afectan a
priori a bastantes Apps, tampoco se soportan todas las APIs sino un
subconjunto de las más importantes y utilizadas. Por último, recordad de
nuevo, proyecto en desarrollo no esta excento de Bugs.

¿Y ahora?

Ahora la parte más importante de todas, hacer de la App, una App Windows de la máxima calidad. Tenemos interoperabilidad con APIs Windows.
Podemos integrar la App con el sistema, utilizar opciones Windows para
compartir, mostrar notificaciones en el Action Center, etc. Nos
centraremos en opciones de esta índole en otro artículo.

Más información

[Offtopic] Nuevos retos profesionales!

Nuevos retos

No suelo compartir nada que no sea directa o indirectamente técnico
mediante el Blog. Pero esta ocasión creo que merece que la comparta con
todos vosotros. En ocasiones, cuando menos te lo esperas, se te cruza
una oportunidad en tu camino que no puedes dejar pasar. A mi se me ha
presentado esa oportunidad. Un gran grupo con grandes ideas, grandes
proyectos, mucha energía y ganas, etc. Por ello, he tomado la decisión
de cambiar de empresa. Tras un tiempo en Bravent donde
sin duda he aprendido y crecido donde dejo a amigos y profesionales,
comenzaré con tremenda ilusión y muchas ganas un nuevo camino en Plain Concepts.

El conocer al equipo humano que me rodeará, los proyectos en los que
podré embarcarme y las posibilidades de crecimiento presentadas han
provocado mi actual decisión. Espero estar a la altura de las
circustancias. Sin duda, daré el 101%.

Más información

[Tips and Tricks] Detectar modo de interacción en Windows 10

Introducción

Windows 10 ha llegado como la culminación en el viaje hacia la convergencia en el desarrollo entre plataformas Windows. Ahora hablamos de Apps Universales
escritas una única vez con un código común tanto para la lógica de
negocio como para la interfaz de usuario. Además, generamos un único
paquete que mantendrá una interfaz consistente y familiar para el
usuario pero adaptada a cada plataforma. Podemos crear apps que
funcionen en todo tipo de dispositivos como teléfonos, tabletas,
portátiles, dispositivos IoT, Surface Hub e incluso HoloLens.

Generamos un único paquete para todas las familias de dispositivos por lo tanto:

  • Debemos utilizar SDKs de extensión para acceder a APIs concretas de una familia de dispositivo.
  • Utilizaremos varias técnicas para adaptar la interfaz de usuario y
    poder otorgar la mejor experiencia de usuario posible en cada familia de
    dispositivo.

Sin embargo, a pesar de adaptar código e interfaz no siempre en cada dispositivo utilizaremos el mismo modo de interacción. Por ejemplo, en una Surface podremos interactuar con la App tanto utilizando teclado y ratón como con los dedos.

¿Podemos detectar el modo de interacción del usuario con nuestra App y actuar en consecuencia?

En este artículo vamos a aprender como detectar el modo de interacción.

Modo de interacción

Podemos acceder a la configuración actual de la ventana de la App utilizando UIViewSettings.GetForCurrentView() donde tenemos acceso a una propiedad llamada UserInteractionMode:

var userInteractiveMode = UIViewSettings.GetForCurrentView().UserInteractionMode;

La propiedad es de tipo enumeración con las siguientes opciones:

  • Mouse: El usuario esta utilizando ratón.
  • Touch: EL usuario utiliza pantalla táctil.
switch (userInteractiveMode)
{
     case UserInteractionMode.Mouse:
          InteractionMode = "Ratón";
          break;
     case UserInteractionMode.Touch:
          InteractionMode = "Táctil";
          break;
}

En función de los valores anteriores podemos realizar adaptaciones de
la interfaz de usuario para facilitar la interacción dependiendo del
modo del usuario. Podéis descargar el ejemplo completo realizado a
continuación:

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Recordar que podéis dejar en los comentarios cualquier tipo de sugerencia o pregunta.

Más información