viernes, 14 de marzo de 2014

Procesos en Windows Phone (I): Introducción y asociación de URIs


1.- Introducción

Al igual que en otros SO móviles como Android, en Windows Phone (WP en adelante) podemos lanzar otros procesos desde nuestra aplicación ya sea para que se ejecuten en foreground (primer plano) o en background (segundo plano).

Los procesos en foreground cubiertos a continuación corresponden a aplicaciones externas a la nuestra, no a la navegación entre páginas dentro de nuestra aplicación. Si ese es el tema que te interesa haces mejor en seguir el siguiente enlace para aprender más sobre la navegación en WP: Navegación en Windows Phone.

Antes de continuar aclararé que no se cubren los aspectos sobre como crear la aplicación en Visual Studio o como lanzarla en un emulador para centrarnos más en la parte del lanzamiento y manejo de procesos. Para aprender sobre esto: Primeros pasos.

Por ultimo aclarar que para no duplicar código y no perder legibilidad los ejemplos de código están unicamente en C#, no hay código en VisualBasic.


2.- Aplicaciones externas: Asociación de URIs

Windows Phone nos proporciona funcionalidad para asociar nuestras aplicaciones a cierto tipo de archivos (para que al abrirlos se lance nuestra aplicación) y para que sean visibles al resto de aplicaciones, que podrán lanzarlas en cualquier momento. Esto se consigue a través de la construcción y registro de una serie de Uniform Resource Identifier (URI en adelante) que identificarán los tipos de archivo y protocolos de comunicación app-to-app que maneja.

A partir de ahora nos centraremos en el segundo caso: protocolos de comunicación app-to-app, aunque la mecánica del primero no es muy distinta.

Empecemos con un ejemplo teórico: imaginemos que tenemos una aplicación que gestiona podcasts (creacion, borrado, actualizaciones...). Sería interesante que cualquier reproductor de audio puediese utilizar nuestra aplicación para no tener que reimplementar toda la funcionalidad especifica de los podcast y se centrase simplemente en reproducir los archivos descargados.

Para ello nuestra aplicación se asociará a un esquema URI y las demás aplicaciones solo tienen que navegar a una URI con ese esquema para lanzar nuestra aplicación. Un ejemplo mas concreto del esquema URI sería:

podcasts:ShowPodcast?ID=42e-aff4
podcasts:ShowAllPodcasts?UpdateEnabled=true&OrderBy=date
podcasts:DisablePodcastUpdate?ID=78dl-gvw42

En los esquemas usados en WP debemos diferenciar dos partes principales que son las que están separadas por los dos puntos (:). La primera es el nombre del esquema, lo que identifica el protocolo utilizado y lo que usa WP para encontrar las aplicaciones que son capaces de responder a esa URI (podcasts en este caso). La segunda parte es totalmente libre, podéis poner lo que queráis. Podemos utilizarla para identificar operaciones dentro del protocolo (ShowPodcast, DisablePodcastUpdate...) y darle argumentos (ID, OrderBy...) como en nuestro ejemplo. Solo debéis tener en cuenta que estas URI serán usadas por otras aplicaciones, lo que significa que otros desarrolladores deberán saber como usarlas sin problemas, asi que tomaros el tiempo necesario para diseñar un buen protocolo.

Antes de seguir a un ejemplo práctico hay que decir que implementar un protocolo que solo utilizará vuestra aplicación no sirve de mucho a no ser que otras aplicaciones quieran usar expresamente tu aplicación. Lo mejor es implementar protocolos ya existentes. Si vamos a desarrollar una aplicación para podcasts, deberíamos usar el/los protocolo/s que usen otras aplicaciones de este tipo.

Cuando hay varias aplicaciones que responden al mismo protocolo y se intenta navegar a una URI perteneciente a este, el sistema le preguntará al usuario que aplicación le gustaría utilizar. Una aplicación que quiera manejar podcasts utilizará el lenguaje (protocolo) más común para esto, lanzando un mensaje dirigido "a quien le pueda interesar". Adaptarnos a ese protocolo en lugar de tener solo uno propio (que no conoce nadie) nos permite competir con otras aplicaciones con funcionalidad similar. Si os interesa aquí podréis ver una lista de protocolos ya existentes: Lista de protocolos. También hay que decir que existe una lista de protocolos reservados por el sistema. Cualquier llamada a uno estos protocolos será atendida por el sistema, ignorando nuestra aplicación por completo.


2.1.-Ejemplo de uso

Vamos a seguir con nuestro ejemplo de los podcasts para este ejemplo de uso. En resumen deberemos seguir una serie de pasos:
  • Registrar el protocolo
  • Implementar un URIMapper
  • Registrar nuestro URIMapper en el frame de nuestra aplicación.

Registrar el protocolo. Esto debemos hacerlo en el WMAppManifest.xml de nuestra aplicación. En el apartado de extensiones (Extensions) añadiremos los protocolos a los que estamos registrados (hasta un máximo de 10):
<Extensions>
  <Protocol Name="podcasts" NavUriFragment="encodedLaunchUri=%s" TaskID="_default" />
</Extensions>

En Name pondríamos el nombre de nuestro protocolo. Los dos atributos restantes deben tener siempre esos valores.

Implementar un URIMapper. Cuando una aplicación navega a una URI como las del apartado anterior, el sistema la codifica usando percent-encoding y se la envía al objeto URIMapper de nuestra aplicación. El objetivo de este URIMapper es traducir esa URI en la URI que identifica la página de la aplicación que responderá a la llamada junto con sus argumentos. Si no la puede traducir devolverá la URI original para que el sistema lance nuestra aplicación de forma normal (como si el usuario la hubiese lanzado desde su menú de aplicaciones).

Por ejemplo veamos las URIs anteriores codificadas:
/Protocol?encodedLaunchUri=podcasts%3AShowPodcast%3FID%3D42e-aff4
/Protocol?encodedLaunchUri=podcasts%3AShowAllPodcasts%3FUpdateEnabled%3Dtrue%26OrderBy%3Ddate
/Protocol?encodedLaunchUri=podcasts%3ADisablePodcastUpdate%3FID%3D78dl-gvw42

Y aquí una posible traducción al dominio de URIs de nuestra aplicación:
/ShowPodcastDetail.xaml?id=42e-aff4
/ShowListPodcasts.xaml?upd_enabled=true&order_by=date
/EditPodcastUpdate.xaml?id=78dl-gvw42&op=disable

Donde la primera parte (hasta el '?') identifica la página y el resto son los argumentos.

Pasemos a la implementación. Creamos un archivo MyUriMapper.cs donde añadiremos el siguiente código:
using System;
using System.Windows.Navigation;

namespace sdkAutoLaunch
{
  class MyUriMapper : UriMapperBase
  {
    private string tempUri;

    public override Uri MapUri(Uri uri){
      // Descodificamos la URI
      tempUri = System.Net.HttpUtility.UrlDecode(uri.ToString());

      // Identificamos la operación
      if (tempUri.Contains("podcasts:ShowPodcast?ID=")){
        // Obtnemos el valor de los argumentos
        int idIndex = tempUri.IndexOf("ID=") + 3;
        string id = tempUri.Substring(categoryIdIndex);

        // Devolvemos la URI de la página a utilizar
        return new Uri("/ShowPodcastDetail.xaml?id=" + id, UriKind.Relative);
      }
      
      // Hacemos lo mismo con el resto de operaciones
      if (tempUri.Contains("podcasts:ShowAllPodcasts?")){
        // ...
      }

      // ...

      // Si no reconocemos la URI la devolvemos
      return uri;
    }
  }
}

Registrar nuestro URIMapper en el frame de nuestra aplicación. Para esto editaremos el método InitializePhoneApplication del archivo App.xaml.cs. Añadiremos la siguiente línea justo después de que se haya inicializado RootFrame.Navigated:
RootFrame.UriMapper = new MyUriMapper();

Un ejemplo de InitializePhoneApplication resultante:
private void InitializePhoneApplication(){
  if (phoneApplicationInitialized)
    return;

  RootFrame = new PhoneApplicationFrame();

  RootFrame.Navigated += CompleteInitializePhoneApplication;

  // Aqui asignamos nuestro URIMapper

  RootFrame.UriMapper = new MyUriMapper();
   RootFrame.NavigationFailed += RootFrame_NavigationFailed;
  phoneApplicationInitialized = true;
}

Ya sabemos como hacer que nuestra aplicación sea capaz de lanzarse según una URI asociada. Solo nos queda saber como otras aplicaciones lanzarían la nuestra (o como nosotros podemos lanzar otras aplicaciones):
private async void LaunchPodcastsViewer(long id){
  Windows.System.Launcher.LaunchUriAsync(new System.Uri("podcasts:NewProducts?ID="+id.ToString()));
}

Con esto solo necesitaríamos encontrar el protocolo adecuado, tanto si estamos desarrollando una aplicación que espera ser llamada como si queremos hacer uso de otras aplicaciones.

No hay comentarios:

Publicar un comentario