Uso di Hub di notifica di Azure per inviare notifiche agli utenti per Android con back-end .NET

Overview

Il supporto per le notifiche push in Azure consente di accedere a un'infrastruttura push facile da usare, multipiattaforma e con scalabilità orizzontale, che semplifica considerevolmente l'implementazione delle notifiche push sia per le applicazioni consumer sia per quelle aziendali per piattaforme mobili. In questa esercitazione viene illustrato come usare Hub di notifica di Azure per inviare notifiche push a un utente specifico dell'app su un dispositivo specifico. Per autenticare i client e generare le notifiche viene usato un back-end di API Web ASP.NET, come illustrato nell'argomento Registrazione dal back-end dell'app. Questa esercitazione si basa sull'hub di notifica creato nell'esercitazione Introduzione ad Hub di notifica (Android) .

Nota

In questa esercitazione si presuppone che l'utente abbia creato e configurato l'hub di notifica come descritto in Introduzione ad Hub di notifica (Android).

Creare il progetto di API Web

Un nuovo back-end WebAPI ASP.NET verrà creato nelle sezioni che seguono e avrà tre scopi principali:

  1. Autenticazione dei client: in seguito verrà aggiunto un gestore di messaggi per autenticare le richieste client e associare l'utente alla richiesta.
  2. Registrazioni delle notifiche client: in seguito si aggiungerà un controllo per gestire le nuove registrazioni in modo che un dispositivo cliente riceva le notifiche. Il nome utente autenticato verrà aggiunto automaticamente alla registrazione come tag.
  3. Invio di notifiche ai client: in seguito si aggiungerà anche un controller per fornire all'utente un modo per attivare un push sicuro ai dispositivi e ai client associati al tag.

I passaggi seguenti mostrano come creare un nuovo back-end WebAPI ASP.NET:

Importante

Se si usa Visual Studio 2015 o versione precedente, prima di procedere con l'esercitazione verificare di avere installato la versione più recente di Gestione pacchetti NuGet. A questo scopo, avviare Visual Studio. Scegliere Estensioni e aggiornamenti dal menu Strumenti. Cercare Gestione pacchetti NuGet per la versione di Visual Studio e verificare che sia installata la versione più recente. In caso contrario, disinstallare e quindi reinstallare Gestione pacchetti NuGet.

Nota

Assicurarsi che sia installato Visual Studio Azure SDK per la distribuzione del sito Web.

  1. Avviare Visual Studio o Visual Studio Express. Fare clic su Esplora server e accedere all'account Azure. Per la creazione delle risorse del sito Web nell'account, in Visual Studio è necessario eseguire l'accesso.
  2. In Visual Studio fare clic su File, Nuovo e quindi Progetto, espandere Modelli e Visual C#, quindi fare clic su Web e Applicazione Web ASP.NET, digitare il nome AppBackend e infine fare clic su OK.

  3. Nella finestra di dialogo Nuovo progetto ASP.NET fare clic su API Web e quindi su OK.

  4. Nella finestra di dialogo Configura app Web di Microsoft Azure scegliere una sottoscrizione e un piano di servizio app già creato. È inoltre possibile scegliere Crea un nuovo piano di servizio app e crearne uno dalla finestra di dialogo. Per questa esercitazione non è necessario disporre di un database. Dopo aver selezionato il piano di servizio app, fare clic su OK per creare il progetto.

Autenticazione di client al back-end WebAPI

In questa sezione si creerà una nuova classe del gestore di messaggi denominata AuthenticationTestHandler per il nuovo back-end. Questa classe è derivata da DelegatingHandler e viene aggiunta come gestore di messaggi per poter elaborare tutte le richieste in arrivo nel back-end.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto AppBackend, scegliere Aggiungi e quindi fare clic su Classe. Assegnare alla nuova classe il nome AuthenticationTestHandler.cs e fare clic su Aggiungi per generarla. Questa classe viene usata per semplicità per autenticare gli utenti mediante l' autenticazione di base . Tenere presente che l'app può usare qualsiasi schema di autenticazione.
  2. In AuthenticationTestHandler.cs aggiungere le istruzioni using seguenti:

     using System.Net.Http;
     using System.Threading;
     using System.Security.Principal;
     using System.Net;
     using System.Text;
     using System.Threading.Tasks;
    
  3. In AuthenticationTestHandler.cs sostituire la definizione di classe AuthenticationTestHandler con il codice seguente:

    Questo gestore autorizza la richiesta quando le tre seguenti condizioni sono tutte vere:

    • La richiesta include un'intestazione Autorizzazione .
    • La richiesta usa l'autenticazione di base .
    • La stringa del nome utente corrisponde alla stringa della password.

      In caso contrario, la richiesta verrà rifiutata. Non si tratta di un vero approccio di autenticazione e autorizzazione. È un esempio molto semplice per questa esercitazione.

      Se il messaggio di richiesta viene autenticato e autorizzato da AuthenticationTestHandler, l'utente di autenticazione di base verrà associato alla richiesta corrente in HttpContext. Le informazioni utente in HttpContext verranno usate da un altro controller (RegisterController) in un secondo momento per aggiungere un tag per la richiesta di registrazione della notifica.

      public class AuthenticationTestHandler : DelegatingHandler { protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var authorizationHeader = request.Headers.GetValues("Authorization").First();

            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
      
                if (verifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
      
            return base.SendAsync(request, cancellationToken);
        }
      
        private bool verifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
      
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
      

      }

      Nota

      Nota sulla sicurezza: la classe AuthenticationTestHandler non fornisce un'effettiva autenticazione. Viene usata solo per imitare l'autenticazione di base e non è sicura. È necessario implementare un meccanismo di autenticazione sicuro nelle applicazioni e nei servizi di produzione.

  4. Aggiungere il codice seguente alla fine del metodo Register nella classe App_Start/WebApiConfig.cs per registrare il gestore di messaggi:

     config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  5. Salvare le modifiche.

Registrazione per le notifiche tramite il back-end WebAPI

In questa sezione si aggiungerà un nuovo controller al back-end WebAPI per gestire le richieste per la registrazione di un utente e un dispositivo per le notifiche tramite la libreria client per gli hub di notifica. Il controller aggiungerà un tag user per l'utente che è stato autenticato e collegato a HttpContext da AuthenticationTestHandler. Il tag avrà il formato della stringa, "username:<actual username>".

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto AppBackend e quindi scegliere Gestisci pacchetti NuGet.
  2. Sul lato sinistro fare clic su Online e cercare Microsoft.Azure.NotificationHubs nella casella di ricerca.
  3. Nell'elenco risultati fare clic su Hub di notifica di Microsoft Azure e quindi su Installa. Completare l'installazione e chiudere la finestra di Gestione pacchetti NuGet.

    Verrà aggiunto un riferimento all’SDK dell’Hub di notifica di Azure mediante il pacchetto NuGet Microsoft.Azure.NotificationHubs.

  4. Ora verrà creato un nuovo file della classe che rappresenta la connessione con l'hub di notifica usato per inviare le notifiche. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Modelli, scegliere Aggiungi e quindi fare clic su Classe. Assegnare alla nuova classe il nome Notifications.cs e quindi fare clic su Aggiungi per generarla.

  5. In Notifications.cs aggiungere l'istruzione using seguente all'inizio del file:

     using Microsoft.Azure.NotificationHubs;
    
  6. Sostituire la definizione di classe Notifications con il codice seguente e assicurarsi di sostituire i due segnaposto con la stringa di connessione (con accesso completo) per l'hub di notifica e con il nome dell'hub (disponibile nel portale di Azure classico):

     public class Notifications
     {
         public static Notifications Instance = new Notifications();
    
         public NotificationHubClient Hub { get; set; }
    
         private Notifications() {
             Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>", 
                                                                          "<hub name>");
         }
     }
    
  7. Verrà quindi creato un nuovo controller denominato RegisterController. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella Controller, scegliere Aggiungi e quindi fare clic su Controller. Fare clic sull'elemento Controller Web API 2 - Vuoto e quindi su Aggiungi. Assegnare alla nuova classe il nome RegisterController e quindi fare di nuovo clic su Aggiungi per generare il controller.

  8. In RegisterController.cs aggiungere le istruzioni using seguenti:

     using Microsoft.Azure.NotificationHubs;
     using Microsoft.Azure.NotificationHubs.Messaging;
     using AppBackend.Models;
     using System.Threading.Tasks;
     using System.Web;
    
  9. Aggiungere il codice seguente all'interno della definizione di classe RegisterController . Si noti che in questo codice viene aggiunto un tag user per l'utente associato a HttpContext. L'utente è stato autenticato e associato a HttpContext dal filtro messaggi aggiunto, AuthenticationTestHandler. È anche possibile aggiungere controlli facoltativi per verificare che l'utente disponga dei diritti per la registrazione per i tag richiesti.

     private NotificationHubClient hub;
    
     public RegisterController()
     {
         hub = Notifications.Instance.Hub;
     }
    
     public class DeviceRegistration
     {
         public string Platform { get; set; }
         public string Handle { get; set; }
         public string[] Tags { get; set; }
     }
    
     // POST api/register
     // This creates a registration id
     public async Task<string> Post(string handle = null)
     {
         string newRegistrationId = null;
    
         // make sure there are no existing registrations for this push handle (used for iOS and Android)
         if (handle != null)
         {
             var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
             foreach (RegistrationDescription registration in registrations)
             {
                 if (newRegistrationId == null)
                 {
                     newRegistrationId = registration.RegistrationId;
                 }
                 else
                 {
                     await hub.DeleteRegistrationAsync(registration);
                 }
             }
         }
    
         if (newRegistrationId == null) 
             newRegistrationId = await hub.CreateRegistrationIdAsync();
    
         return newRegistrationId;
     }
    
     // PUT api/register/5
     // This creates or updates a registration (with provided channelURI) at the specified id
     public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
     {
         RegistrationDescription registration = null;
         switch (deviceUpdate.Platform)
         {
             case "mpns":
                 registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                 break;
             case "wns":
                 registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                 break;
             case "apns":
                 registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                 break;
             case "gcm":
                 registration = new GcmRegistrationDescription(deviceUpdate.Handle);
                 break;
             default:
                 throw new HttpResponseException(HttpStatusCode.BadRequest);
         }
    
         registration.RegistrationId = id;
         var username = HttpContext.Current.User.Identity.Name;
    
         // add check if user is allowed to add these tags
         registration.Tags = new HashSet<string>(deviceUpdate.Tags);
         registration.Tags.Add("username:" + username);
    
         try
         {
             await hub.CreateOrUpdateRegistrationAsync(registration);
         }
         catch (MessagingException e)
         {
             ReturnGoneIfHubResponseIsGone(e);
         }
    
         return Request.CreateResponse(HttpStatusCode.OK);
     }
    
     // DELETE api/register/5
     public async Task<HttpResponseMessage> Delete(string id)
     {
         await hub.DeleteRegistrationAsync(id);
         return Request.CreateResponse(HttpStatusCode.OK);
     }
    
     private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
     {
         var webex = e.InnerException as WebException;
         if (webex.Status == WebExceptionStatus.ProtocolError)
         {
             var response = (HttpWebResponse)webex.Response;
             if (response.StatusCode == HttpStatusCode.Gone)
                 throw new HttpRequestException(HttpStatusCode.Gone.ToString());
         }
     }
    
  10. Salvare le modifiche.

Invio di notifiche dal back-end WebAPI

In questa sezione si aggiungerà un nuovo controller che espone un modo per consentire ai dispositivi client di inviare una notifica in base al tag username usando la libreria di gestione del servizio Hub di notifica di Azure nel back-end WebAPI ASP.NET.

  1. Creare un altro nuovo controller denominato NotificationsController. Crearlo seguendo la stessa procedura usata per creare RegisterController nella sezione precedente.
  2. In NotificationsController.cs aggiungere le istruzioni using seguenti:

     using AppBackend.Models;
     using System.Threading.Tasks;
     using System.Web;
    
  3. Aggiungere il codice seguente alla classe NotificationsController .

    Questo codice invia un tipo di notifica basato sul parametro pns (Platform Notification Service). Il valore to_tag viene usato per impostare il tag username nel messaggio. Questo tag deve corrispondere a un tag username di una registrazione dell'hub di notifica attiva. Il messaggio di notifica viene estratto dal corpo della richiesta POST ed è formattato per il PNS di destinazione.

    A seconda del PNS (Platform Notification Service) usato dai dispositivi supportati per ricevere le notifiche, sono supportate notifiche diverse con formati diversi. Ad esempio nei dispositivi Windows è possibile usare una notifica di tipo avviso popup con WNS che non è supportata direttamente da un altro PNS. Il back-end deve pertanto formattare la notifica come una notifica supportata per il PNS dei dispositivi che si intende supportare. Usare quindi l'API di invio appropriata per la classe NotificationHubClient

     public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
     {
         var user = HttpContext.Current.User.Identity.Name;
         string[] userTag = new string[2];
         userTag[0] = "username:" + to_tag;
         userTag[1] = "from:" + user;
    
         Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
         HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
         switch (pns.ToLower())
         {
             case "wns":
                 // Windows 8.1 / Windows Phone 8.1
                 var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                             "From " + user + ": " + message + "</text></binding></visual></toast>";
                 outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                 break;
             case "apns":
                 // iOS
                 var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                 outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                 break;
             case "gcm":
                 // Android
                 var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                 outcome = await Notifications.Instance.Hub.SendGcmNativeNotificationAsync(notif, userTag);
                 break;
         }
    
         if (outcome != null)
         {
             if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                 (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
             {
                 ret = HttpStatusCode.OK;
             }
         }
    
         return Request.CreateResponse(ret);
     }
    
  4. Premere F5 per eseguire l'applicazione e verificare l'accuratezza del lavoro fino a questo punto. L'app dovrebbe avviare un Web browser e visualizzare la home page di ASP.NET.

Pubblicare il nuovo back-end WebAPId

  1. L'app verrà ora distribuita in un sito Web Azure in modo da renderla accessibile da tutti i dispositivi. Fare clic con il pulsante destro del mouse sul progetto AppBackend e scegliere Pubblica.
  2. Selezionare Servizio app di Microsoft Azure come destinazione di pubblicazione e quindi fare clic su Pubblica. Verrà visualizzata la finestra di dialogo Crea servizio app, che consente di creare tutte le risorse di Azure necessarie per eseguire l'app Web ASP.NET in Azure.

  3. Nella finestra di dialogo Crea servizio app selezionare l'account Azure. Fare clic su Modifica tipo e selezionare App Web. Mantenere il valore di Nome app Web assegnato e selezionare la sottoscrizione, il gruppo di risorse e il piano di servizio app. Fare clic su Crea.

  4. Prendere nota della proprietà URL sito nella scheda Riepilogo. Si farà riferimento a quest'URL come endpoint back-end più avanti in questa esercitazione. Fare clic su Pubblica.

  5. Al termine della procedura guidata, l'app Web ASP.NET viene pubblicata in Azure e avviata nel browser predefinito. L'applicazione sarà visibile in Servizi app di Azure.

L'URL usa il nome dell'app Web specificato in precedenza, con il formato http://.azurewebsites.net.

Creare il progetto Android

Il passaggio successivo consiste nella creazione dell'applicazione per Android.

  1. Seguire l'esercitazione Introduzione ad Hub di notifica (Android) per creare e configurare l'app per la ricezione di notifiche push da GCM.
  2. Aprire il file res/layout/activity_main.xml e sostituire il contenuto con le definizioni seguenti.

    Verranno aggiunti nuovi controlli EditText per l'accesso come utente. Viene aggiunto anche un campo per un tag username che farà parte delle notifiche inviate:

     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
         android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
         android:paddingRight="@dimen/activity_horizontal_margin"
         android:paddingTop="@dimen/activity_vertical_margin"
         android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    
     <EditText
         android:id="@+id/usernameText"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:ems="10"
         android:hint="@string/usernameHint"
         android:layout_above="@+id/passwordText"
         android:layout_alignParentEnd="true" />
     <EditText
         android:id="@+id/passwordText"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:ems="10"
         android:hint="@string/passwordHint"
         android:inputType="textPassword"
         android:layout_above="@+id/buttonLogin"
         android:layout_alignParentEnd="true" />
     <Button
         android:id="@+id/buttonLogin"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/loginButton"
         android:onClick="login"
         android:layout_above="@+id/toggleButtonGCM"
         android:layout_centerHorizontal="true"
         android:layout_marginBottom="24dp" />
     <ToggleButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textOn="WNS on"
         android:textOff="WNS off"
         android:id="@+id/toggleButtonWNS"
         android:layout_toLeftOf="@id/toggleButtonGCM"
         android:layout_centerVertical="true" />
     <ToggleButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textOn="GCM on"
         android:textOff="GCM off"
         android:id="@+id/toggleButtonGCM"
         android:checked="true"
         android:layout_centerHorizontal="true"
         android:layout_centerVertical="true" />
     <ToggleButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textOn="APNS on"
         android:textOff="APNS off"
         android:id="@+id/toggleButtonAPNS"
         android:layout_toRightOf="@id/toggleButtonGCM"
         android:layout_centerVertical="true" />
     <EditText
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/editTextNotificationMessageTag"
         android:layout_below="@id/toggleButtonGCM"
         android:layout_centerHorizontal="true"
         android:hint="@string/notification_message_tag_hint" />
     <EditText
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/editTextNotificationMessage"
         android:layout_below="@+id/editTextNotificationMessageTag"
         android:layout_centerHorizontal="true"
         android:hint="@string/notification_message_hint" />
     <Button
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/send_button"
         android:id="@+id/sendbutton"
         android:onClick="sendNotificationButtonOnClick"
         android:layout_below="@+id/editTextNotificationMessage"
         android:layout_centerHorizontal="true" />
     </RelativeLayout>
    
  3. Aprire il file res/values/strings.xml e sostituire la definizione send_button con le righe seguenti per ridefinire la stringa per send_button e aggiungere stringhe per gli altri controlli:

     <string name="usernameHint">Username</string>
     <string name="passwordHint">Password</string>
     <string name="loginButton">1. Log in</string>
     <string name="send_button">2. Send Notification</string>
     <string name="notification_message_tag_hint">
         Recipient username tag
     </string>
    

    A questo punto il layout grafico del file main_activity.xml dovrebbe essere simile al seguente:

  4. Creare una nuova classe denominata RegisterClient nello stesso pacchetto della classe MainActivity. Usare il codice seguente per il nuovo file di classe.

     import java.io.IOException;
     import java.io.UnsupportedEncodingException;
     import java.util.Set;
    
     import org.apache.http.HttpResponse;
     import org.apache.http.HttpStatus;
     import org.apache.http.client.ClientProtocolException;
     import org.apache.http.client.HttpClient;
     import org.apache.http.client.methods.HttpPost;
     import org.apache.http.client.methods.HttpPut;
     import org.apache.http.client.methods.HttpUriRequest;
     import org.apache.http.entity.StringEntity;
     import org.apache.http.impl.client.DefaultHttpClient;
     import org.apache.http.util.EntityUtils;
     import org.json.JSONArray;
     import org.json.JSONException;
     import org.json.JSONObject;
    
     import android.content.Context;
     import android.content.SharedPreferences;
     import android.util.Log;
    
     public class RegisterClient {
         private static final String PREFS_NAME = "ANHSettings";
         private static final String REGID_SETTING_NAME = "ANHRegistrationId";
         private String Backend_Endpoint;
         SharedPreferences settings;
         protected HttpClient httpClient;
         private String authorizationHeader;
    
         public RegisterClient(Context context, String backendEnpoint) {
             super();
             this.settings = context.getSharedPreferences(PREFS_NAME, 0);
             httpClient =  new DefaultHttpClient();
             Backend_Endpoint = backendEnpoint + "/api/register";
         }
    
         public String getAuthorizationHeader() {
             return authorizationHeader;
         }
    
         public void setAuthorizationHeader(String authorizationHeader) {
             this.authorizationHeader = authorizationHeader;
         }
    
         public void register(String handle, Set<String> tags) throws ClientProtocolException, IOException, JSONException {
             String registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
    
             JSONObject deviceInfo = new JSONObject();
             deviceInfo.put("Platform", "gcm");
             deviceInfo.put("Handle", handle);
             deviceInfo.put("Tags", new JSONArray(tags));
    
             int statusCode = upsertRegistration(registrationId, deviceInfo);
    
             if (statusCode == HttpStatus.SC_OK) {
                 return;
             } else if (statusCode == HttpStatus.SC_GONE){
                 settings.edit().remove(REGID_SETTING_NAME).commit();
                 registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
                 statusCode = upsertRegistration(registrationId, deviceInfo);
                 if (statusCode != HttpStatus.SC_OK) {
                     Log.e("RegisterClient", "Error upserting registration: " + statusCode);
                     throw new RuntimeException("Error upserting registration");
                 }
             } else {
                 Log.e("RegisterClient", "Error upserting registration: " + statusCode);
                 throw new RuntimeException("Error upserting registration");
             }
         }
    
         private int upsertRegistration(String registrationId, JSONObject deviceInfo)
                 throws UnsupportedEncodingException, IOException,
                 ClientProtocolException {
             HttpPut request = new HttpPut(Backend_Endpoint+"/"+registrationId);
             request.setEntity(new StringEntity(deviceInfo.toString()));
             request.addHeader("Authorization", "Basic "+authorizationHeader);
             request.addHeader("Content-Type", "application/json");
             HttpResponse response = httpClient.execute(request);
             int statusCode = response.getStatusLine().getStatusCode();
             return statusCode;
         }
    
         private String retrieveRegistrationIdOrRequestNewOne(String handle) throws ClientProtocolException, IOException {
             if (settings.contains(REGID_SETTING_NAME))
                 return settings.getString(REGID_SETTING_NAME, null);
    
             HttpUriRequest request = new HttpPost(Backend_Endpoint+"?handle="+handle);
             request.addHeader("Authorization", "Basic "+authorizationHeader);
             HttpResponse response = httpClient.execute(request);
             if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                 Log.e("RegisterClient", "Error creating registrationId: " + response.getStatusLine().getStatusCode());
                 throw new RuntimeException("Error creating Notification Hubs registrationId");
             }
             String registrationId = EntityUtils.toString(response.getEntity());
             registrationId = registrationId.substring(1, registrationId.length()-1);
    
             settings.edit().putString(REGID_SETTING_NAME, registrationId).commit();
    
             return registrationId;
         }
     }
    

    Questo componente implementa le chiamate REST necessarie per contattare il back-end dell'app allo scopo di effettuare la registrazione per le notifiche push. Archivia inoltre in locale i registrationId creati dall'hub di notifica, come illustrato in Registrazione dal back-end dell'app. Si noti che usa un token di autorizzazione memorizzato nell'archivio locale quando si fa clic sul pulsante Log in .

  5. Nella classe MainActivity rimuovere o impostare come commento il campo privato per NotificationHub e aggiungere un campo per la classe RegisterClient e una stringa per l'endpoint del back-end ASP.NET. Assicurarsi di sostituire <Enter Your Backend Endpoint> con l'endpoint back-end effettivo ottenuto in precedenza: Ad esempio: http://mybackend.azurewebsites.net.

     //private NotificationHub hub;
     private RegisterClient registerClient;
     private static final String BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
    
  6. Nella classe MainActivity, all'interno del metodo onCreate, rimuovere o impostare come commento l'inizializzazione del campo hub e la chiamata al metodo registerWithNotificationHubs. Aggiungere quindi il codice per inizializzare un'istanza della classe RegisterClient . Il metodo deve contenere le seguenti righe:

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
    
         MyHandler.mainActivity = this;
         NotificationsManager.handleNotifications(this, SENDER_ID, MyHandler.class);
         gcm = GoogleCloudMessaging.getInstance(this);
    
         //hub = new NotificationHub(HubName, HubListenConnectionString, this);
         //registerWithNotificationHubs();
    
         registerClient = new RegisterClient(this, BACKEND_ENDPOINT);
    
         setContentView(R.layout.activity_main);
     }
    
  7. Nella classe MainActivity eliminare o impostare come commento tutto il metodo registerWithNotificationHubs. Non verrà usato in questa esercitazione.
  8. Aggiungere le istruzioni import seguenti al file MainActivity.java .

     import android.widget.Button;
     import java.io.UnsupportedEncodingException;
     import android.content.Context;
     import java.util.HashSet;
     import android.widget.Toast;
     import org.apache.http.client.ClientProtocolException;
     import java.io.IOException;
     import org.apache.http.HttpStatus;
    
  9. Aggiungere quindi i metodi seguenti per gestire l'evento clic del pulsante Log in e inviare le notifiche push.

     @Override
     protected void onStart() {
         super.onStart();
         Button sendPush = (Button) findViewById(R.id.sendbutton);
         sendPush.setEnabled(false);
     }
    
     public void login(View view) throws UnsupportedEncodingException {
         this.registerClient.setAuthorizationHeader(getAuthorizationHeader());
    
         final Context context = this;
         new AsyncTask<Object, Object, Object>() {
             @Override
             protected Object doInBackground(Object... params) {
                 try {
                     String regid = gcm.register(SENDER_ID);
                     registerClient.register(regid, new HashSet<String>());
                 } catch (Exception e) {
                     DialogNotify("MainActivity - Failed to register", e.getMessage());
                     return e;
                 }
                 return null;
             }
    
             protected void onPostExecute(Object result) {
                 Button sendPush = (Button) findViewById(R.id.sendbutton);
                 sendPush.setEnabled(true);
                 Toast.makeText(context, "Logged in and registered.",
                         Toast.LENGTH_LONG).show();
             }
         }.execute(null, null, null);
     }
    
     private String getAuthorizationHeader() throws UnsupportedEncodingException {
         EditText username = (EditText) findViewById(R.id.usernameText);
         EditText password = (EditText) findViewById(R.id.passwordText);
         String basicAuthHeader = username.getText().toString()+":"+password.getText().toString();
         basicAuthHeader = Base64.encodeToString(basicAuthHeader.getBytes("UTF-8"), Base64.NO_WRAP);
         return basicAuthHeader;
     }
    
     /**
      * This method calls the ASP.NET WebAPI backend to send the notification message
      * to the platform notification service based on the pns parameter.
      *
      * @param pns     The platform notification service to send the notification message to. Must
      *                be one of the following ("wns", "gcm", "apns").
      * @param userTag The tag for the user who will receive the notification message. This string
      *                must not contain spaces or special characters.
      * @param message The notification message string. This string must include the double quotes
      *                to be used as JSON content.
      */
     public void sendPush(final String pns, final String userTag, final String message)
             throws ClientProtocolException, IOException {
         new AsyncTask<Object, Object, Object>() {
             @Override
             protected Object doInBackground(Object... params) {
                 try {
    
                     String uri = BACKEND_ENDPOINT + "/api/notifications";
                     uri += "?pns=" + pns;
                     uri += "&to_tag=" + userTag;
    
                     HttpPost request = new HttpPost(uri);
                     request.addHeader("Authorization", "Basic "+ getAuthorizationHeader());
                     request.setEntity(new StringEntity(message));
                     request.addHeader("Content-Type", "application/json");
    
                     HttpResponse response = new DefaultHttpClient().execute(request);
    
                     if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                         DialogNotify("MainActivity - Error sending " + pns + " notification",
                             response.getStatusLine().toString());
                         throw new RuntimeException("Error sending notification");
                     }
                 } catch (Exception e) {
                     DialogNotify("MainActivity - Failed to send " + pns + " notification ", e.getMessage());
                     return e;
                 }
    
                 return null;
             }
         }.execute(null, null, null);
     }
    

    Il gestore login per il pulsante **Log inRegisterClient genera un token di autenticazione di base usando il nome utente e la password di input. Si noti che rappresenta qualsiasi token usato dallo schema di autenticazione, quindi usa ** per chiamare il back-end per la registrazione.

    Il metodo sendPush chiama il back-end per attivare una notifica sicura per l'utente in base al tag user. Il servizio di notifica della piattaforma a cui è destinato sendPush dipende dalla stringa pns passata.

  10. Nella classe MainActivity aggiornare il metodosendNotificationButtonOnClick per chiamare il metodo sendPush con i servizi di notifica della piattaforma selezionati dell'utente, come indicato di seguito.

    /**
     * Send Notification button click handler. This method sends the push notification
     * message to each platform selected.
     *
     * @param v The view
     */
    public void sendNotificationButtonOnClick(View v)
            throws ClientProtocolException, IOException {
    
        String nhMessageTag = ((EditText) findViewById(R.id.editTextNotificationMessageTag))
                .getText().toString();
        String nhMessage = ((EditText) findViewById(R.id.editTextNotificationMessage))
                .getText().toString();
    
        // JSON String
        nhMessage = "\"" + nhMessage + "\"";
    
        if (((ToggleButton)findViewById(R.id.toggleButtonWNS)).isChecked())
        {
            sendPush("wns", nhMessageTag, nhMessage);
        }
        if (((ToggleButton)findViewById(R.id.toggleButtonGCM)).isChecked())
        {
            sendPush("gcm", nhMessageTag, nhMessage);
        }
        if (((ToggleButton)findViewById(R.id.toggleButtonAPNS)).isChecked())
        {
            sendPush("apns", nhMessageTag, nhMessage);
        }
    }
    

Eseguire l'applicazione

  1. Eseguire l'applicazione su un dispositivo o un emulatore tramite Android Studio.
  2. Nell'app per Android immettere un nome utente e una password. Devono avere lo stesso valore di stringa e non devono contenere spazi o caratteri speciali.
  3. Nell'app per Android fare clic su Log in. Attendere un avviso popup che indica Logged in and registered. Verrà abilitato il pulsante Send Notification .

  4. Fare clic sugli interruttori per abilitare tutte le piattaforme in cui è stata eseguita l'app ed è stato registrato un utente.
  5. Immettere il nome dell'utente che riceverà il messaggio di notifica. L'utente dovrà essere registrato per le notifiche nei dispositivi di destinazione.
  6. Immettere un messaggio che l'utente riceverà come messaggio di notifica push.
  7. Fare clic su Send Notification. Ogni dispositivo che ha una registrazione con il tag username corrispondente riceverà la notifica push.