Notificaciones a Aplicaciones Windows Phone desde SharePoint Parte II

Escrito por  Adrian Diaz Cervera

Este artículo es la continuación del artículo del número pasado en el explicábamos como enviar notificaciones PUSH desde SharePoint. En este número vamos a explicar cómo tenemos que desarrollar una APP de Windows Phone 7.5 y 8 para poder recibir las notificaciones desde SharePoint tanto en su versión 2010 como en la nueva versión 2013.

Requisitos Previos

Descargar el SDK Client SharePoint para Windows Phone 7.1 que facilita la comunicación de la aplicación móvil con SharePoint

Desarrollo de la APP de Windows Phone

La APP que vamos a desarrollar solamente incluye dos partes. Por un lado vamos a explicar cómo habilitar nuestra aplicación para poder recibir notificaciones PUSH y por otro lado como podemos leer los datos de las Listas que hay en SharePoint. Dados estos requerimientos podemos realizar una aplicación que funcione tanto para Windows Phone 7.5 y Windows Phone 8; primero tenemos que saber que tendremos que hacer dos aplicaciones distintas, pero no hace falta que dupliquemos todo el código. La cuestión es utilizar los elementos comunes que tienen ambas versiones y de esta forma hacer una aplicación que nos cueste mucho menos de mantener, sea escalable y de esta forma llegue a un número mayor de usuarios.

Para hacer que el código que realicemos sea compatible en ambas versiones lo que vamos a realizar en primer lugar es crearnos un proyectos de Bibliotecas portables en el que vamos a poner los elementos comunes que van a tener ambas APP. Que va a ser todo a excepción de las Vistas ya que no es lo mismo diseñar para una APP que tiene distinto tamaño de pantalla; el resto del código fuente va a ser igual.

Seleccionamos el tipo de SO que vamos a utilizar para crear la aplicación eligiendo la versión 7.1. Lo hacemos así porque en la versión 8 incluye todo lo que había antes y además las nuevas mejoras que conlleva.

A este proyecto le tenemos que añadir las siguientes referencias (Microsoft.Phone, Microsoft.Phone.Interop y Microsoft.SharePoint.Client.Phone)

Referencias a añadir al proyecto

En primer lugar lo que vamos a implementar es una clase "Notification" que tendrá una variable de tipo HttpNotificationChannel que es la que se encargara de comunicarse con el MPNS. En primer lugar nos crearemos un método OpenNotificationChannel cuya función es buscar si el canal de comunicación esta creado. En caso de que no esté creado se debe crear. Hay que saber que una aplicación de WP solo puede tener un único canal de comunicaciones abierto y este no se puede comunicar con el resto de aplicaciones que hay en el teléfono.   Además se le añadirán manejadores a los siguientes eventos:

  • ChannelUriUpdate que se lanzará si el MPNS cambia la dirección URI de notificaciones.
  • ErrorOcurredse lanzará si ocurre un error en la aplicación.
  • httpChannel_ShellToastNotificationReceived se lanzará en el momento que recibamos una notificación TOAST.

La primera vez que ejecutamos el canal no tenemos aún la dirección URI y solo tendremos una dirección URI valida en el momento que se lance el evento ChannelUriUpdate, por lo que hasta que no tengamos esta dirección valida no podremos registrar nuestro dispositivo en la aplicación.

1public static void OpenNotificationChannel( bool isInitialRegistration)
2
3{
4
5try   {
6
7// Buscamos si el canal ya esta creado
8
9httpChannel = HttpNotificationChannel .Find(ChannelName);
10
11// Si no se encuentra el canal creamos uno
12
13if (httpChannel == null )
14
15{
16
17httpChannel = new HttpNotificationChannel (ChannelName);
18
19//Añadimos los eventos asociados al HttpNotificationChannel
20
21// Hasta que no salte el evento ChannelUriUpdate como no tenemos URI
22
23// no podemos subscribir el dispositivo Sharepoint
24
25AddChannelEventHandlers();
26
27httpChannel.Open();
28
29}
30
31else
32
33{
34
35//En caso de que el canal ya exista asociamos los eventos al HttpNotificationChannal
36
37AddChannelEventHandlers();
38
39// En caso de que sea la primera vez que se ejecute suscribiremos al Servicio
40
41if (isInitialRegistration)
42
43{
44
45SubscribeToService();
46
47}
48
49}
50
51}
52
53catch ( Exception ex)
54
55{
56
57MessageBox .Show(ex.Message, "Error Abriendo el Canal" , MessageBoxButton .OK);
58
59CloseChannel();
60
61}
62
63}
64

Una vez ya tenemos el canal abierto, el siguiente paso a realizar es implementar los manejadores:

**ChannelUriUpdate:**En este evento lo que vamos a realizar es que cada vez que se ejecute, subscribiremos este dispositivo. Pueden darse dos situaciones: la primera vez en la que registramos el dispositivo y para la que valdría el siguiente método. En la segunda, simplemente realizamos la subscripción.

1/// <summary>
2
3/// Suscribir el servicio en una lista de Sharepoint
4
5/// </summary>
6
7private static void SubscribeToService()
8
9{
10
11Guid deviceAppInstanceId = GetSettingValue< Guid >(DeviceAppIdKey, false );
12
13Context.Load(Context.Web, w => w.Title, w => w.Description);
14
15PushNotificationSubscriber pushSubscriber =
16
17Context.Web.RegisterPushNotificationSubscriber(deviceAppInstanceId, httpChannel.ChannelUri.AbsoluteUri);
18
19Context.Load(pushSubscriber);
20
21Context.ExecuteQueryAsync
22
23(
24
25( object sender, ClientRequestSucceededEventArgs args) =>
26
27{
28
29SetRegistrationStatus( true );
30
31if (!httpChannel.IsShellTileBound) {httpChannel.BindToShellTile();}
32
33if (!httpChannel.IsShellToastBound) {httpChannel.BindToShellToast();}
34
35ShowMessage( string .Format( "Subscripcion Correcta registrada: {0}" , pushSubscriber.User.LoginName), "Realizado" );
36
37},
38
39( object sender, ClientRequestFailedEventArgs args) =>
40
41{
42
43ShowMessage(args.Exception.Message, "Error Subscribiendo" );
44
45});
46
47}
48

Pero el problema es que la dirección URI puede cambiar, ¿y entonces que hacemos cuando se modifica un dirección? Pues bien primero llamaremos a la siguiente función que lo que hace es actualizar la Dirección URI en SharePoint, en caso de que no exista no hace nada. Posteriormente, si procede, llamamos a la función de arriba.

1/// <summary>
2
3/// Actualizo el Canar Uri en el Servidor
4
5/// </summary>
6
7private static void UpdateChannelUriOnServer()
8
9{
10
11Guid deviceAppInstanceId = GetSettingValue< Guid >(DeviceAppIdKey, false );
12
13Context.Load(Context.Web, w => w.Title, w => w.Description);
14
15PushNotificationSubscriber subscriber = Context.Web.GetPushNotificationSubscriber(deviceAppInstanceId);
16
17Context.Load(subscriber);
18
19Context.ExecuteQueryAsync(
20
21( object sender1, ClientRequestSucceededEventArgs args1) =>
22
23{
24
25subscriber.ServiceToken = httpChannel.ChannelUri.AbsolutePath;
26
27subscriber.Update();
28
29Context.ExecuteQueryAsync(
30
31( object sender2, ClientRequestSucceededEventArgs args2) =>
32
33{
34
35ShowMessage( "Channel URI updated on server." , "Success" );
36
37},
38
39( object sender2, ClientRequestFailedEventArgs args2
40
41) =>
42
43{
44
45ShowMessage(args2.Exception.Message, "Error Upating Channel URI" );
46
47});
48
49},
50
51( object sender1, ClientRequestFailedEventArgs args1) =>
52
53{
54
55
56
57});
58
59}
60
61
62
63/// <summary>
64
65/// En el momento que recibimos una notificacion Toast
66
67/// </summary>
68
69/// <param name="sender"></param>
70
71/// <param name="e"></param>
72
73static void httpChannel\_ShellToastNotificationReceived( object sender, NotificationEventArgs e)
74
75{
76
77if (e.Collection != null )
78
79{
80
81Dictionary < string , string > collection = ( Dictionary < string , string >)e.Collection;
82
83ShellToast toast = new ShellToast ();
84
85toast.Title = collection[ "wp:Text1" ];
86
87toast.Content = collection[ "wp:Text2" ];
88
89
90
91ShowMessage( string .Format( "Titulo: {0}\r\nAutor: {1}" , toast.Title, toast.Content), "Toast Notification" );
92
93}
94
95}
96
97Para almacenar el GUID de la aplicación, y si hemos registrado o no la aplicación, hemos utilizado el almacenamiento local que nos ofrece Windows Phone ya que nos proporciona un grado de seguridad altísimo, puesto que ninguna otra aplicación puede acceder a zonas de memoria usadas por el sistema o por otras aplicaciones.
98
99El siguiente paso que le añadiremos a esta biblioteca es crearnos el ViewModel que vamos a realizar en primer lugar nos creamos una clase ItemArticleViewModel que va a contener las propiedades que queremos mostrar en nuestro caso "Titulo" del articulo y "Autor" la clase quedaría de la siguiente forma (hay que añadirle la parte de notificación para que se modifiquen los valores en los bindings de la capa de vista); el resto es como si fuera una clase normal:
100
101public class ItemArticleViewModel : INotifyPropertyChanged
102
103{
104
105public string Title { get ; set ; }
106
107public string Author { get ; set ; }
108
109
110
111public event PropertyChangedEventHandler PropertyChanged;
112
113private void NotifyPropertyChanged( String propertyName)
114
115{
116
117PropertyChangedEventHandler handler = PropertyChanged;
118
119if ( null != handler)
120
121{
122
123handler( this , new PropertyChangedEventArgs (propertyName));
124
125}
126
127}
128
129}
130

Una vez creado la definición del modelo nos creamos la capa de donde se obtienen los datos y ahí es donde entra en juego SharePoint. Antes de empezar a ver cómo hacerlos. Hay que saber las opciones que tenemos para obtener los datos de un SharePoint:

  1. A través del SDK de SharePoint para Windows Phone 7.5 (que es compatible también para la versión 8).
  2. Utilizando el API REST de SharePoint.

En este ejemplo vamos a realizarlo a través la opción 1, entonces nos creamos una clase MainArticleViewModel que va a tener una esctructura como la siguiente:

1public class MainArticleViewModel : INotifyPropertyChanged
2
3{
4
5public MainArticleViewModel()
6
7{
8
9this .Items = new ObservableCollection < ItemArticleViewModel >();
10
11}
12
13
14
15/// <summary>
16
17/// Colección para objetos ItemViewModel.
18
19/// </summary>
20
21public ObservableCollection < ItemArticleViewModel > Items { get ; private set ; }
22
23
24
25public string SampleProperty
26
27{
28
29get ;
30
31set ;
32
33}
34
35
36
37public bool IsDataLoaded
38
39{
40
41get ;
42
43private set ;
44
45}
46
47public event PropertyChangedEventHandler PropertyChanged;
48
49private void NotifyPropertyChanged( String propertyName)
50
51{
52
53PropertyChangedEventHandler handler = PropertyChanged;
54
55if ( null != handler)
56
57{
58
59handler( this , new PropertyChangedEventArgs (propertyName));
60
61}
62
63}
64
65Y para completar esta clase le falta la  función LoadData( ). En esta función es donde vamos a realizar la llamada a SharePoint, y se hace de una forma muy similar a la que se utiliza en las llamadas Cliente en 2010 y convertiremos los elementos de la lista en objetos de nuestro ViewModel. La función quedaría así:
66
67public void LoadData()
68
69{
70
71ClientContext context = new ClientContext ( "http://compartimoos" );
72
73context.Credentials =  new Authenticator();
74
75List articlesList = context.Web.Lists.GetByTitle( "Articulos" );
76
77CamlQuery query = new CamlQuery ();
78
79query.ViewXml = @"<Query><Eq>
80
81<FieldRef Name='Revista' />
82
83<Value Type='Text'>'Numero13'</Value>
84
85</Eq>
86
87</Where></Query>" ;
88
89ListItemCollection itemsList = articlesList.GetItems(query);
90
91context.Load(itemsList);
92
93context.ExecuteQuery();
94
95if (itemsList.Count > 0)
96
97{
98
99foreach ( ListItem item in itemsList)
100
101{
102
103ItemArticleViewModel itemArticle= new ItemArticleViewModel ();
104
105itemArticle.Author=item[ "Author" ].ToString();
106
107itemArticle.Title=item[ "Title" ].ToString();
108
109this .Items.Add(itemArticle);
110
111}
112
113}
114

Creando las aplicaciones

Una vez tenemos la clase con toda la funcionalidad disponible vamos a crear la aplicación de Windows Phone. Lo primero que tenemos que hacer es agregar la referencia a la librería que habíamos creado anteriormente.

A continuación nos vamos a la página MainPage.xaml y a la página le añadimos un objeto de tipo "Pivots " y en los "Bindings" tendremos que poner los nombres de los campos de nuestro modelo en nuestro caso Article y Phone

1<controls:Pivot Title="MI APLICACIÓN">
2
3<!--Elemento Pivot uno-->
4
5<controls:PivotItem Header="primero">
6
7<!--Lista de líneas dobles con ajuste de texto-->
8
9<ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
10
11<ListBox.ItemTemplate>
12
13<DataTemplate>
14
15<StackPanel Margin="0,0,0,17" Width="432" Height="78">
16
17<TextBlock Text="{Binding Article}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
18
19<TextBlock Text="{Binding Author}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
20
21</StackPanel>
22
23</DataTemplate>
24
25</ListBox.ItemTemplate>
26
27</ListBox>
28
29</controls:PivotItem>
30
31</controls:Pivot>
32

El siguiente paso es irnos a al APP.CS y añadir este propiedad:

1public static MainArticleViewModel ViewModel
2
3{
4
5get
6
7{
8
9// Retrasar la creación del modelo de vista hasta que sea necesario
10
11if (viewModel == null )
12
13viewModel = new MainArticleViewModel ();
14
15
16
17return viewModel;
18
19}
20
21}
22

Y dentro del evento cuando se active añadir esta llamada:

1if (! App .ViewModel.IsDataLoaded)
2
3{
4
5App .ViewModel.LoadData();
6
7}
8

Ahora nos dirigimos a la MainPage.cs y aquí tendremos que establecer el contexto de esta pantalla que con el View Model quedaría de la siguiente forma:

1// Constructor
2
3public MainPage()
4
5{
6
7InitializeComponent();
8
9
10
11// Establecer el contexto de datos del control ListBox control en los datos de ejemplo
12
13DataContext = App .ViewModel;
14
15this .Loaded += new RoutedEventHandler (MainPage\_Loaded);
16
17}
18
19
20
21// Cargar datos para los elementos ViewModel
22
23private void MainPage\_Loaded( object sender, RoutedEventArgs e)
24
25{
26
27if (! App .ViewModel.IsDataLoaded)
28
29{
30
31App .ViewModel.LoadData();
32
33}
34
35}
36

Una vez realizado esto si ejecutamos nuestra aplicación se visualizará la siguiente pantalla:

Previsualización de la aplicación en el emulador de Windows Phone

Ahora lo que falta es añadir que esta aplicación pueda recibir notificaciones PUSH dentro de nuestra aplicación, para ello en el main llamamos al método OpenNotificacion de la clase Notificacion. Como hemos visto anteriormente, lo que hace es lo siguiente:

  • Crear el Canal de comunicaciones.
  • En el momento que tenemos una dirección URI valida subscribimos la aplicación a SharePoint.
  • Cuando recibimos una notificación la mostramos en la aplicación (en caso de que tengamos la aplicación cerrada se visualizara la notificación en la parte superior de la pantalla como en las siguientes imágenes):
Ejemplo de notificación Notificación PUSH con la APP en segundo plano

Esto solo es un ejemplo de como se pueden enviar notificaciones desde una lista de Sharepoint a aplicaciones Windows Phone. Para una aplicación "profesional" tendriamos que crear una pantalla de configuración de estas notificaciones para que el usuario tenga la potestad de elegir si quiere o no quiere recibir notificaciones. Tambien quedaria pendiente la forma en la que se deja de recibir notificiaciones (hay un metodo unsubscriber tambien) . Otra cosa a tener en cuenta es que con aplicaciones de Windows Phone, no hay notificación de eventos  cuando una aplicación se desinstala por lo que en tendremos que implementar alguna función que nos haga saber si todos los dispositivos móviles  que tenemos registrados estan "activos". Pero esto ya se deja a la intriga del lector.

¿Cómo implementamos la aplicación si estamos trabajando con un SharePoint 2010?

El desarrollo es al 90% igual para un sistema que para el otro, la unica diferencia es que en 2010 no tenemos el metodo de subscribir al dispositivo a una lista y por lo tanto lo tendremos que hacer de forma manual. Viendo la solución que habia planteado con anterioridad esta claro lo que tenemos que hacer. Hay que insertar un elemento en una lista de SharePoint y esto utilizando las librerias clientes de SharePoint es muy sencillo de hacer.

Lo que va a realizar la siguiente función es: en primer lugar consultamos  si tenemos este elemento en la lista y en caso que afirmativo actualizamos la dirección URI y sino existe insertamos el elemento en la lista.

1private void SubscribeToService( string Guid, string ChannelUri)
2
3{
4
5ClientContext context = new ClientContext ( "http://compartimoos" );
6
7
8
9if (!existGuid(Guid, context))
10
11{
12
13InsertData(Guid, ChannelUri, context);
14
15}
16
17else
18
19{
20
21UpadteData(Guid, ChannelUri, context);
22
23}
24
25}
26
27private bool existGuid( string Guid, ClientContext context)
28
29{
30
31bool res = false ;
32
33
34
35List mensajesList = context.Web.Lists.GetByTitle( "Subscriptores" );
36
37CamlQuery query = new CamlQuery ();
38
39query.ViewXml = string .Format( @"<Query><Eq>
40
41<FieldRef Name='Guid' />
42
43<Value Type='Text'>{0}</Value>
44
45</Eq>
46
47</Where></Query>"
48
49,Guid);
50
51ListItemCollection itemsList = mensajesList.GetItems(query);
52
53context.Load(itemsList);
54
55context.ExecuteQuery();
56
57if (itemsList.Count > 0) { res = true ; }
58
59return res;
60
61}
62
63private static void InsertData( string Guid, string ChannelUri, ClientContext context)
64
65{
66
67List mensajesList = context.Web.Lists.GetByTitle( "Subscriptores" );
68
69ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation ();
70
71ListItem oListItem = mensajesList.AddItem(itemCreateInfo);
72
73oListItem[ "Guid" ] = Guid;
74
75oListItem[ "ChannelUri" ] = ChannelUri;
76
77oListItem.Update();
78
79context.ExecuteQuery();
80
81}
82

Conclusiones

En este articulos hemos visto lo sencillo que es crear Apps para Windows Phone tanto en la versión 7.5 y versión 8 con el origen de datos almacenados en SharePoint tanto en la versión 2010 como en la 2013. Ademas se le puede añadir complementos para que la interacción APP con SharePoint este presente y de esta forma el usuario pueda estar al tanto de lo que se realiza. Con lo que es un muy buen punto para incluir dentro de nuestros desarrollos profesionales.

Referencias

Adrián Díaz Cervera

SharePoint Developer at Encamina

MCPD SharePoint 2010 MAP y MCC 2012

http://blogs.encamina.com/desarrollandosobresharepoint

adiaz@encamina.com

@AdrianDiaz81

Siguemos en LinkedInSiguemos en Twitter
Powered by  ENCAMINA