[Windows Phone 8.1] El Action Center

Introducción

Los Live Tiles disponibles en la plataforma Windows son una excelente
y elegante forma de notificar al usuario con información relevante para
el mismo. Sin embargo, no todas las aplicaciones están ancladas al
inicio y a veces el usuario puede perder información. Para solventar
esto llega el Action Center, un lugar donde ver todas
las notificaciones de todas las aplicaciones incluso de las no ancladas
al incio además de poder acceder a configuración básica del sistema como
el modo vuelo, WiFi o Bluetooth.

Con el SDK de Windows Phone 8.1 tenemos a nuestra disposición varias
APIs que permiten administrar las notificaciones del centro de
actividades (Action Center). En este artículo vamos a conocer las APIs
disponibles.

¿Te apuntas?

Manos a la Obra

Comenzamos creando un nuevo proyecto:

Partimos de la plantilla Blank App para centrar nuestra atención en la gestión de notificaciones. 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.

En el artículo actual vamos a centrarnos en varios puntos:

  • Enviar una notificación Toast local que mostrará un mensaje (como hsata ahora) y además quedara registrada en el Action Center.
  • Enviar un nuevo tipo de notificación Toast llamada “fantasma” que no
    muestra mensaje sino que aparece directamente en el Action Center.
  • Vamos a aprender como gestiona las colecciones de notificaciones el Action Center.
  • Vamos a gestionar notificaciones, además de añadirlas, vamos a ver como eliminarlas.

Para cubrir estos objetivos, vamos a añadir múltiples vistas, cada
una de ellas, centrada en uno de los objetivos anteriores. Añadimos:

  • ToastView: Desde donde enviar una notificación Toast.
  • GhostView : Desde donde enviar una notificación “fantasma”.
  • QueueView: Desde aqui enviaremos un grupo de notificaciones.
  • EditView: Desde aqui gestionaremos múltiples notificaciones.

Nos centramos en la vista principal de la aplicación:

<ScrollViewer>
     <StackPanel Margin="12">
            <TextBlock Text="ACTION CENTER" FontSize="32"
                       Margin="0, 0, 0, 25"/>
            <TextBlock Text="Enviaremos
una notificación toast local. EL popup de la notificación aparecerá y
automáticamente tambien se enviará al action center."
                       TextWrapping="Wrap" />
            <Button Content="Notificación Toast" Command="{Binding ToastCommand}"/>
            <TextBlock Text="Enviaremos
una notificación toast fantasma local. Usamos la propiedad SupressPopup
del Toast para que solo aparezca en el action center."
                       TextWrapping="Wrap" />
            <Button Content="Notificación Toast fantasma" Command="{Binding GhostCommand}"/>
            <TextBlock Text="Toda
aplicación tiene una cantidad finita de espacio en el action center. En
este ejemplo enviamos múltiples notificaciones para demostrar su
funcionamiento."
                       TextWrapping="Wrap" />
            <Button Content="Sistema de colas" Command="{Binding QueueCommand}"/>
            <TextBlock Text="Gracias a NotificationManager.History podemos eliminar notificaciones del action center."
                       TextWrapping="Wrap" />
            <Button Content="Gestión de notificaciones" Command="{Binding EditCommand}"/>
     </StackPanel>
</ScrollViewer>

Como podemos ver, contamos con un listado de botones que ejecutan
comandos de la viewmodel, que realizarán la navegación a sus respectivas
vistas. Vemos el código de la viewmodel:

private ICommand _ghostCommand;
private ICommand _queueCommand;
private ICommand _editCommand;
private ICommand _toastCommand;
 
public ICommand GhostCommand
{
     get { return _ghostCommand = _ghostCommand ?? new DelegateCommand(GhostCommandDelegate); }
}
 
public ICommand QueueCommand
{
     get { return _queueCommand = _queueCommand ?? new DelegateCommand(QueueCommandDelegate); }
}
 
public ICommand EditCommand
{
     get { return _editCommand = _editCommand ?? new DelegateCommand(EditCommandDelegate); }
}
 
public ICommand ToastCommand
{
     get { return _toastCommand = _toastCommand ?? new DelegateCommand(ToastCommandDelegate); }
}
 
public void GhostCommandDelegate()
{
     AppFrame.Navigate(typeof (GhostView));
}
 
public void QueueCommandDelegate()
{
     AppFrame.Navigate(typeof (QueueView));
}
 
public void EditCommandDelegate()
{
     AppFrame.Navigate(typeof (EditView));
}
 
public void ToastCommandDelegate()
{
     AppFrame.Navigate(typeof(ToastView));
}

Aún nada específico de notificaciones o el Action Center, sencillamente
la estructura base de nuestro ejemplo. El resultado de lo realizado
hsata aqui es:

Enviando notificaciones Toast

Para poder probar el centro de actividades tenemos que enviar
notificaciones. La forma más sencilla de enviar notificaciones es
utilizando notificaciones locales del sistema. Para la gestión correcta de todo vamos a crear un servicio llamado LocalNotificationService:

public class LocalNotificationService : ILocalNotificationService
{
 
}

En la definición del servicio comenzamos con un método imprescindible, verificar si podemos enviar notificaciones o no:

/// <summary>
/// Verifica si podemos enviar notificaciones o no.
/// Verifica si estan habilitadas las notificaciones toast en la App.
/// </summary>
/// <returns></returns>
bool CanSendToasts();

Implementamos la interfaz:

public bool CanSendToasts()
{
     bool canSend = true;
     var notifier = ToastNotificationManager.CreateToastNotifier();
 
     if (notifier.Setting != NotificationSetting.Enabled)
          canSend = false;
 
     return canSend;
}

Antes de continuar, vamos a analizar que hacemos en el método anterior.

Utilizamos la clase ToastNotificationManager.
Esta clase tiene como objetivo principal crear objetos de tipo
ToastNotifier usados para generar notificaciones del sistema. Además,
nos permite acceder al contenido XML de las plantillas de notificaciones
para poder acceder y modificar su contenido. En este caso, utilizamos
el método  CreateToastNotifier que crea e inicializa una instancia de ToastNotification. Verificamos basicamente si las notificaciones estan habilitadas o no.

Para que la aplicación sea capaz de gestionar notificaciones Toast
debemos activar la opción en el archivo de manifiesto del paquete.

Nuestro objetivo principal en el servicio es crear notificaciones. Ese será el objetivo del siguiente método:

/// <summary>
/// Utiliza la plantilla ToastText02. Plantilla sencilla, se puede ver el catálogo en: http://msdn.microsoft.com/en-us/library/windows/apps/hh761494.aspx
/// </summary>
/// <param name="toastHeading"></param>
/// <param name="toastBody"></param>
/// <param name="tag"></param>
/// <param name="group"></param>
/// <returns></returns>
public ToastNotification CreateToast(string toastHeading, string toastBody, string tag, string group)
{
     // Usamos la plantilla ToastText02.
     ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
 
      // Obtenemos el xml que configura el toast para poder acceder a sus propiedades.
      XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
 
      //Buscamos el text dentro del contenido.
      XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
 
      // Establecemos el texto.
      toastTextElements[0].AppendChild(toastXml.CreateTextNode(toastHeading));
      toastTextElements[1].AppendChild(toastXml.CreateTextNode(toastBody));
 
      IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
      ((XmlElement)toastNode).SetAttribute("duration", "long");
 
      // Creamos la notificación.
      ToastNotification toast = new ToastNotification(toastXml);
 
      // Si nos llega tag se lo asignamos.
      if (!string.IsNullOrEmpty(tag))
           toast.Tag = tag;
 
      // Si nos llega grupo se lo asignamos.
      if (!string.IsNullOrEmpty(group))
          toast.Group = group;
 
      return toast;
}

En el código anterior, se crea una notificación Toast local utilizando la plantilla ToastText02. Esta plantilla cuenta con un máximo de dos elementos de texto. Es una notificación simple pero bastante usada.

NOTA: Podéis acceder a el catálogo de plantillas de notificación del sistema.

Tras crear la notificación volvemos a utilizar a la clase
ToastNotificationManager para acceder al contenido XML de la
notificación y añadir el texto recibido como parámetros en la
notificación.

Además, podemos recibir etiqueta o grupo para la notificación. Veremos cuando y para que se utilizarán.

Bien, el método anterior crea notificaciones locales pero… ¿cómo las enviamos?

Vamos a crear otro método en nuestro servicio que se encargue de crear la notificación y lanzarla:

/// <summary>
/// Además de crear la notificación, la lanza y muestra.
/// </summary>
/// <param name="toastHeading"></param>
/// <param name="toastBody"></param>
/// <param name="suppressPopup"></param>        
/// <param name="tag"></param>
/// <param name="group"></param>
public void ShowToast(string toastHeading, string toastBody, string tag, string group, bool suppressPopup)
{
     ToastNotification toast = CreateToast(toastHeading, toastBody, tag, group);
 
     // SuppressPopup = true no apareceel popup, se envia directamente el action center.
     toast.SuppressPopup = suppressPopup;
 
     // Envia la notificación toast.
     ToastNotificationManager.CreateToastNotifier().Show(toast);
}

Para enviar la notificación localmente utilizamos el método ToastNotifier.Show(ToastNotification).

Hasta aquí nuestro servicio es lo suficientemente potente como para enviar notificaciones locales. Asi pues… !¿a que esperamos?!

Nos centramos en el primero de nuestros puntos, enviar una notificación local normal. Recordad que creamos una vista llamada ToastView que vinculamos (usamos Ioc, con un ViewModelLocator) a su viewmodel:

DataContext="{Binding ToastViewModel, Source={StaticResource Locator}}"

Nos centramos en la vista de ToastView. Basicamente añadimos un botón que nos permita enviar la notificación:

<Grid>
     <StackPanel Margin="12">
          <TextBlock Text="DEMO:" FontSize="32" />
          <TextBlock TextWrapping="Wrap">
                Enviaremos
una notificación toast local. EL popup de la notificación aparecerá y
automáticamente tambien se enviará al action center.
          </TextBlock>
          <Button Content="Enviar Notificación" Command="{Binding SendNotificationCommand}"/>
     </StackPanel>
</Grid>

El resultado es:

Nos centramos en la viewmodel de esta vista:

private ILocalNotificationService _localNotificationService;
private ICommand _sendNotificationCommand;
 
public ToastViewModel(ILocalNotificationService localNotificationService)
{
     _localNotificationService = localNotificationService;
}
 
public ICommand SendNotificationCommand
{
     get { return _sendNotificationCommand = _sendNotificationCommand ?? new DelegateCommand(SendNotificationCommandDelegate); }
}
 
public void SendNotificationCommandDelegate()
{
     if (_localNotificationService.CanSendToasts())
          _localNotificationService.ShowToast("Toast Simple", "EL popup de la notificación aparecerá", string.Empty, string.Empty, false);
}

El botón lanza un comando que utiliza el servicio de notificaciones
locales llamando al método ShowToast que analizamos previamente. Creará
una notificación Toast local y la mostrará:

Aparecerá un mensaje como el de la captura anterior. Además:

Automáticamente se mostrará también en el centro de actividades.

Notificaciones fantasma

Aunque mostrar un mensaje emergente con las notificaciones es una
funcionalidad fantástica además de una manera ideal de atraer la
atención del usuario sobre algo importante que esta ocurriendo en
nuestra aplicación. De hecho, es una forma tan efectiva de llamar la
atención del usuario que si mostramos demasiados mensajes lograremos el
objetivo contrario al buscado, resultaremos pesados.

¿Y si en lugar de mostrar un mensaje emergente con cada notificación, las registramos directamente en el centro de actividades?

Pues ahora es algo posible. Veamos como sería. Comenzamos creando la interfaz de la vista GhostView:

<Grid>
     <StackPanel Margin="12">
          <TextBlock Text="DEMO:" FontSize="32" />
          <TextBlock TextWrapping="Wrap">
                Enviaremos una notificación toast "fantasma" local. Usamos la propiedad SupressPopup del Toast para que solo aparezca en el action center.
          </TextBlock>
          <Button Content="Enviar Notificación Fantasma" Command="{Binding SendGhostNotificationCommand}"/>
      </StackPanel>
</Grid>

El resultado:

Nos centramos en la viewmodel de la vista:

private ILocalNotificationService _localNotificationService;
private ICommand _sendGhostNotificationCommand;
 
public GhostViewModel(ILocalNotificationService localNotificationService)
{
     _localNotificationService = localNotificationService;
}
 
public ICommand SendGhostNotificationCommand
{
     get
{ return _sendGhostNotificationCommand = _sendGhostNotificationCommand
?? new DelegateCommand(SendGhostNotificationCommandDelegate); }
}
 
public void SendGhostNotificationCommandDelegate()
{
     if (_localNotificationService.CanSendToasts())
          _localNotificationService.ShowToast("Toast Simple", "EL popup de la notificación NO aparecerá", string.Empty, string.Empty, true);
}

De nuevo contamos con un comando que lanza el método ShowToast
de nuestro servicio de notificaciones locales. Sin embargo, en esta
ocasión el método cuenta con un parámetro distinto al caso anterior. El
último parámetro es un booleano que áfecta a ka propiedad SuppressPopup.
Esta propiedad indica si se suprime el mensaje emergente de la
notificación pero manteniendo al usuario informado mediante el centro de
actividades:

La cola de notificaciones del Action Center

El centro de actividades tiene límites. Una misma aplicación puede mantener hasta 20 notificaciones como límite. Cada aplicación gestiona su cola de notificaciones en el centro de actividades siguiente el principio FIFO.

¿Qué quiere decir esto?

Cuando se envia una nueva notificación de la aplicación al centro de
actividades y ya se ha llegado al límite, se elimina automáticamente la
notificación más antigua.

NOTA: En caso de tener más de 20 notificaciones
se muestra un mensaje “Más notificaciones” para avisar al usuario. Al
pulsarlo se abre la aplicación.

Creamos la interfaz que nos permita enviar múltiples notificaciones:

<Grid>
     <StackPanel Margin="12">
          <TextBlock Text="DEMO:" FontSize="32" />
          <TextBlock TextWrapping="Wrap">
                Toda
aplicación tiene una cantidad finita de espacio en el action center. En
este ejemplo enviamos múltiples notificaciones para demostrar su
funcionamiento.
          </TextBlock>
          <Button Content="Enviar múltiples notificaciones" Command="{Binding SendNotificationsCommand}"/>
     </StackPanel>
</Grid>

El resultado es:

Nos centramos en la viewmodel de la vista:

El pulsar el botón ejecutamos un comando de la viewmodel en la que en bucle lanzamos el evento ShowToast de nuestro servicio 25 veces (declarado en constante).

private const int toastsToSend = 25;
 
private ILocalNotificationService _localNotificationService;
private ICommand _sendNotificationsCommand;
 
public QueueViewModel(ILocalNotificationService localNotificationService)
{
     _localNotificationService = localNotificationService;
}
 
public ICommand SendNotificationsCommand
{
     get { return _sendNotificationsCommand = _sendNotificationsCommand ?? new DelegateCommand(SendNotificationsCommandDelegate); }
}
 
public void SendNotificationsCommandDelegate()
{
     if (_localNotificationService.CanSendToasts())
     {
          // Enviamos múltiples notificaciones
          for (int i = 0; i < toastsToSend; i++)
          {
               _localNotificationService.ShowToast(string.Format("Toast {0}", i + 1), "Contenido", string.Empty, string.Empty, true);
          }
     }
}

El resultado es el siguiente:

Como ya mencionamos previamente, cada aplicación cuenta con una cola
de hasta 20 notificaciones mostrando la opción “más notificaciones” en
caso de existir más.

Eliminar notificaciones del Action Center

Hasta ahora hemos visto como se envían notificaciones Toast y notificaciones Toast fantasmas
y su registro en el centro de actividades asi como la gestión de colas
que lleva a cabo el mismo. A continuación, continuaremos viendo como
realizar gestión de notificaciones en el centro de actividades.

Creamos la interfaz de esta última parte del ejemplo:

<Grid>
     <StackPanel Margin="12">
          <TextBlock Text="DEMO:" FontSize="32" />
          <TextBlock TextWrapping="Wrap">
                Gracias a NotificationManager.History podemos eliminar notificaciones del action center.
          </TextBlock>
          <Button Content="Enviar notificaciones" Command="{Binding SendNotificationsCommand}" />
          <Button Content="Eliminar por Tag" Command="{Binding RemoveTagCommand}" />
          <Button Content="Eliminar por grupo" Command="{Binding RemoveGroupCommand}" />
          <Button Content="Eliminar todas las notificaciones" Command="{Binding RemoveAllCommand}" />
      </StackPanel>
</Grid>

Sencilla, cuatro botones que nos permiten enviar notificaciones (con
etiquetas y grupos que veremos a continuación) y eliminarlas:

Nos centramos en el primer botón. Al pulsarlo ejecutará un comando de la viewmodel:

private ICommand _sendNotificationsCommand;
 
public ICommand SendNotificationsCommand
{
     get { return _sendNotificationsCommand = _sendNotificationsCommand ?? new DelegateCommand(SendNotificationsCommandDelegate); }
}
 
public void SendNotificationsCommandDelegate()
{
     _localNotificationService.ShowToast("Toast Tag", "Contenido", "Tag", string.Empty, false);
 
     _localNotificationService.ShowToast("Toast Group", "Contenido", string.Empty, "Group", false);
 
     _localNotificationService.ShowToast("Toast Tag&Group", "Contenido", "Tag", "Group", false);
}

El comando lanzará tres notificaciones:

  • La primera de ellas con etiqueta llamada “Tag”.
  • La segunda lanzará una notificación con grupo llamado “Group”.
  • La última será una notificación con etiqueta y grupo, “Tag” y “Group” respectivamente.

ToastNotification cuenta con las propiedades Tag y Group que permiten administrar facilmente las notificaciones:

Las propiedades Tag y Group de la notificación nos
permiten quitar notificaciones de forma específica o grupos de
notificaciones. Para ello, vamos a añadir nuevos métodos en nuestro
servicio de notificaciones locales:

/// <summary>
/// Elimina notificaciones toast por tag o por grupo.
/// </summary>
/// <param name="tag"></param>
/// <param name="group"></param>
public void RemoveToast(string tag, string group)
{
     if (!string.IsNullOrEmpty(group) && string.IsNullOrEmpty(tag))
          ToastNotificationManager.History.RemoveGroup(group);
     else if (string.IsNullOrEmpty(group) && !string.IsNullOrEmpty(tag))
          ToastNotificationManager.History.Remove(tag);
     else
          ToastNotificationManager.History.Remove(tag, group);
}
 
/// <summary>
/// Elimina todas las notificaciones toast.
/// </summary>
public void RemoveAllToasts()
{
     ToastNotificationManager.History.Clear();
}

Usamos los métodos Remove de ToastNotificationHistory para quitar notificaciones específicas o grupos de notificaciones.

Creamos un método llamado RemoveToast que nos permite eliminar notificaciones por etiqueta, por grupo o por ambas propiedades. Utilizamos el método ToastNotificationHistory.Remove(string)  para eliminar una notificación por etiqueta. Para eliminar una notificación por grupo utilizamos el método ToastNotificationHistory.RemoveGroup(string). Por último, para eliminar notificaciones por etiqueta y grupo utilizamos ToastNotificationHistory.Remove(string, string).

NOTA: No podemos crear un objeto de tipo
ToastNotificationHistory directamente pero podemos obtener una
referencia a la instancia por medio de la propiedad History de la clase ToastNotificationManager.

El segundo método creado, RemoveAllToasts, utiliza el método ToastNotificationHistory.Clear() para quitar todas las notificaciones del centro de activiades.

El segundo botón ejecutarán un comando en la viewmodel:

private ICommand _removeTagCommand;
 
public ICommand RemoveTagCommand
{
     get { return _removeTagCommand = _removeTagCommand ?? new DelegateCommand(RemoveTagCommandDelegate); }
}
 
public void RemoveTagCommandDelegate()
{
     _localNotificationService.RemoveToast("Tag", string.Empty);
}

Que utilizará el método RemoveToast pasandole como parámetro la etiqueta correspondiente. El segundo botón volverá a ejecutar un comando utilizando el método RemoveToast de nuestro servicio de notificaciones locales:

private ICommand _removeGroupCommand;
 
public ICommand RemoveGroupCommand
{
     get { return _removeGroupCommand = _removeGroupCommand ?? new DelegateCommand(RemoveGroupCommandDelegate); }
}
    
public void RemoveGroupCommandDelegate()
{
     _localNotificationService.RemoveToast(string.Empty, "Group");
}

En este caso pasándole como parámetro el grupo correspondiente. Por
último, el último botón eliminará todas las notificaciones. En este caso
utilizaremos el método RemoveAllToasts de nuestro servicio:

private ICommand _removeAllCommand;
 
public ICommand RemoveAllCommand
{
     get { return _removeAllCommand = _removeAllCommand ?? new DelegateCommand(RemoveAllCommandDelegate); }
}
 
public void RemoveAllCommandDelegate()
{
     _localNotificationService.RemoveAllToasts();
}

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

Más información

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *