Kurz: Odesílání nabízených oznámení konkrétním aplikacím pro Android pomocí služby Azure Notification Hubs

Poznámka:

Informace o vyřazení a migraci služby Firebase Cloud Messaging najdete v tématu Migrace služby Google Firebase Cloud Messaging.

V tomto kurzu se dozvíte, jak pomocí služby Azure Notification Hubs posílat nabízená oznámení konkrétním uživatelům aplikace na konkrétním zařízení. K ověřování klientů a generování oznámení se používá back-end ASP.NET WebAPI, jak je znázorněno v článku s doprovodnými materiály popisujícím registraci z back-endu aplikace. Tento kurz vychází z centra oznámení, které jste vytvořili v kurzu: Nabízená oznámení do zařízení s Androidem pomocí služby Azure Notification Hubs a služby Firebase Cloud Messaging.

V tomto kurzu provedete následující kroky:

  • Vytvoření projektu back-endového webového rozhraní API pro ověřování uživatelů
  • Aktualizace aplikace pro Android
  • Otestování aplikace

Požadavky

Před tímto kurzem dokončete nabízená oznámení do zařízení s Androidem pomocí služby Azure Notification Hubs a služby Firebase Cloud Messaging.

Vytvoření projektu WebAPI

Následující části popisují vytvoření nového back-endu ASP.NET WebAPI. Tento proces má tři hlavní účely:

  • Ověřování klientů: Přidáte popisovač zprávy, který bude ověřovat požadavky klientů a přiřazovat uživatele k požadavkům.
  • Registrace k oznámením pomocí back-endu WebAPI: Přidáte kontroler, který bude zpracovávat nové registrace klientských zařízení k přijímání oznámení. Ověřené uživatelské jméno se automaticky přidá do registrace jako značka.
  • Odesílání oznámení klientům: Přidáte kontroler, který uživatelům umožní aktivovat zabezpečené nabízení do zařízení a klientů přidružených ke značce.

Vytvořte nový back-end webového rozhraní API ASP.NET Core 6.0 pomocí následujících akcí:

Pokud to chcete zkontrolovat, spusťte sadu Visual Studio. V nabídce Nástroje vyberte Rozšíření a aktualizace. Vyhledejte Správce balíčků NuGet ve vaší verzi sady Visual Studio a ujistěte se, že máte nejnovější verzi. Pokud vaše verze není nejnovější verzí, odinstalujte ji a pak znovu nainstalujte Správce balíčků NuGet.

Screenshot of the Extensions and Updates dialog box with the NuGet Package manage for Visual Studios package highlighted.

Poznámka:

Ujistěte se, že máte nainstalovanou sadu Azure SDK pro vývoj pro web.

  1. Spusťte sadu Visual Studio nebo Visual Studio Express.

  2. Vyberte Průzkumník serveru a přihlaste se ke svému účtu Azure. Abyste mohli vytvářet prostředky webu, musíte být přihlášení.

  3. V nabídce Soubor v sadě Visual Studio vyberte Nový>projekt.

  4. Do vyhledávacího pole zadejte webové rozhraní API .

  5. Vyberte šablonu projektu základního webového rozhraní API ASP.NET a vyberte Další.

  6. V dialogovém okně Konfigurovat nový projekt pojmenujte projekt AppBackend a vyberte Další.

  7. V dialogovém okně Další informace :

    • Ověřte, že rozhraní je .NET 6.0 (dlouhodobá podpora).
    • Ověřte, že je zaškrtnuté políčko Použít kontrolery (zrušení zaškrtnutí pro použití minimálních rozhraní API).
    • Zrušte zaškrtnutí políčka Povolit podporu OpenAPI.
    • Vyberte Vytvořit.

Odebrání souborů šablony WeatherForecast

  1. Odeberte ukázkové soubory WeatherForecast.cs a controllers/WeatherForecastController.cs z nového projektu AppBackend .
  2. Otevřete vlastnosti\launch Nastavení.json.
  3. Změňte vlastnosti launchUrl z weatherforcast na appbackend.

V okně Konfigurovat webovou aplikaci Microsoft Azure vyberte předplatné a pak v seznamu Plán služby App Service proveďte jednu z následujících akcí:

  • Vyberte plán služby Aplikace Azure, který jste už vytvořili.
  • Vyberte možnost Vytvořit nový plán App Service a pak tento plán vytvořte.

Pro účely tohoto kurzu nepotřebujete databázi. Jakmile vyberete plán služby App Service, výběrem OK vytvořte projekt.

The Configure Microsoft Azure Web App window

Pokud tuto stránku nevidíte pro konfiguraci plánu služby App Service, pokračujte kurzem. Můžete ji nakonfigurovat při pozdějším publikování aplikace.

Ověřování klientů v back-endu WebAPI

V této části vytvoříte pro nový back-end novou třídu popisovače zprávy AuthenticationTestHandler. Tato třída je odvozená od třídy DelegatingHandler a přidaná jako popisovač zprávy, aby mohla zpracovávat všechny požadavky přicházející na back-end.

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt AppBackend, vyberte Přidat a pak vyberte Třída.

  2. Pojmenujte novou třídu AuthenticationTestHandler.cs a pak ji výběrem Přidat vygenerujte. Tato třída bude pro zjednodušení ověřovat uživatele pomocí základního ověřování. Vaše aplikace může používat jakékoli schéma ověřování.

  3. V souboru AuthenticationTestHandler.cs přidejte následující příkazy using:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. V souboru AuthenticationTestHandler.cs nahraďte definici třídy AuthenticationTestHandler následujícím kódem:

    Tato obslužná rutina ověří požadavek při splnění těchto tří podmínek:

    • Požadavek obsahuje autorizační hlavičku.
    • Požadavek používá základní ověřování.
    • Řetězce uživatelského jména a hesla jsou stejné.

    Jinak bude požadavek zamítnut. Při tomto ověření se nepoužívá správný přístup k ověřování a autorizaci. Je to jenom jednoduchý příklad pro účely tohoto kurzu.

    Pokud třída AuthenticationTestHandler ověří a autorizuje zprávu požadavku, uživatel základního ověřování se připojí k aktuálnímu požadavku v objektu HttpContext. Informace o uživateli v objektu HttpContext později použije jiný kontroler (RegisterController) pro přidání značky do požadavku na registraci oznámení.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> 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;
        }
    }
    

    Poznámka:

    Poznámka k zabezpečení: Třída AuthenticationTestHandler nezajišťuje skutečné ověřování. Používá se pouze k napodobení základního ověřování a není bezpečná. Ve svých produkčních aplikacích a službách musíte implementovat mechanismus zabezpečeného ověřování.

  5. Pokud chcete zaregistrovat obslužnou rutinu zprávy, přidejte na konec Register metody do souboru Program.cs následující kód:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Uloží vaše změny.

Registrace k oznámením pomocí back-endu WebAPI

V této části přidáte do back-endu WebAPI nový kontroler, který bude zpracovávat požadavky na registraci uživatele a zařízení k oznámením pomocí klientské knihovny pro centra oznámení. Kontroler přidá značku uživatele pro uživatele, který byl ověřen a připojen k objektu HttpContext třídou AuthenticationTestHandler. Značka bude mít formát řetězce "username:<actual username>".

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt AppBackend a pak vyberte Spravovat balíčky NuGet.

  2. V levém podokně vyberte Online a pak do pole Hledat zadejte Microsoft.Azure.NotificationHubs.

  3. V seznamu výsledků vyberte Microsoft Azure Notification Hubs a pak vyberte Nainstalovat. Dokončete instalaci a pak zavřete okno Správce balíčků NuGet.

    Tato akce přidá referenci na sadu SDK služby Azure Notification Hubs pomocí balíčku NuGet Microsoft.Azure.Notification Hubs.

  4. Vytvořte nový soubor třídy, která představuje propojení s centrem událostí sloužícím k odesílání oznámení. V Průzkumníku řešení klikněte pravým tlačítkem na složku Modely, vyberte Přidat a pak vyberte Třída. Pojmenujte novou třídu Notifications.cs a pak ji výběrem Přidat vygenerujte.

    The Add New Item window

  5. Na začátek souboru Notifications.cs přidejte následující příkaz using:

    using Microsoft.Azure.NotificationHubs;
    
  6. Nahraďte definici třídy Notifications následujícím kódem a nahraďte dva zástupné symboly připojovacím řetězcem (pro úplný přístup) vašeho centra událostí a názvem centra (najdete ho na webu Azure Portal):

    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>");
        }
    }
    

    Důležité

    Než budete pokračovat, zadejte název a defaultFullSharedAccessSignature vašeho centra.

  7. Dále vytvořte nový kontroler RegisterController. V Průzkumníku řešení klikněte pravým tlačítkem na složku Kontrolery, vyberte Přidat a pak vyberte Kontroler.

  8. Vyberte kontroler rozhraní API – prázdný a pak vyberte Přidat.

  9. Do pole Název kontroleru zadejte RegisterController a pojmenujte tak novou třídu, pak vyberte Přidat.

    The Add Controller window.

  10. V souboru RegisterController.cs přidejte následující příkazy using:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Do definice třídy RegisterController přidejte následující kód. V tomto kódu přidáváte značku uživatele pro uživatele, který je připojený k objektu HttpContext. Tento uživatel byl ověřen a připojen k objektu HttpContext filtrem zpráv AuthenticationTestHandler, který jste vytvořili. Můžete také přidat volitelné kontroly pro ověření, že uživatel má práva pro registraci k požadovaným značkám.

    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 "fcm":
                registration = new FcmRegistrationDescription(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());
        }
    }
    
  12. Uloží vaše změny.

Odesílání oznámení z back-endu WebAPI

V této části přidáte nový kontroler, který zveřejňuje způsob odesílání oznámení klientskými zařízeními. Oznámení je založené na značce uživatelského jména, kterou používá knihovna .NET Notification Hubs v back-endu ASP.NET WebAPI.

  1. Vytvořte další nový kontroler NotificationsController stejným způsobem, kterým jste v předchozí části vytvořili RegisterController.

  2. V souboru NotificationsController.cs přidejte následující příkazy using:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Do třídy NotificationsController přidejte následující metodu:

    Tento kód odesílá typ oznámení na základě parametru pns systému oznámení platformy. Hodnota to_tag slouží k nastavení značky username (uživatelské jméno) pro zprávu. Tato značka musí odpovídat značce uživatelského jména aktivní registrace k centru událostí. Zpráva oznámení se přetáhne z textu požadavku POST a naformátuje se pro cílový systém oznámení platformy.

    V závislosti na systému oznámení platformy, který vaše zařízení používá k přijímání oznámení, jsou podporována oznámení v různých formátech. Například na zařízeních s Windows byste mohli použít informační zprávu pomocí Služby nabízených oznámení Windows, kterou ostatní systémy oznámení platformy přímo nepodporují. V takovém případě musí váš back-end formátovat oznámení na podporované oznámení pro systémy oznámení platformy zařízení, která chcete podporovat. Ve třídě NotificationHubClient pak použijte vhodné rozhraní API pro odesílání.

    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 "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(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. Stisknutím klávesy F5 aplikaci spusťte a ověřte, že jste zatím postupovali správně. Aplikace otevře webový prohlížeč a zobrazí se na domovské stránce ASP.NET.

Publikování nového back-endu WebAPI

Dále tuto aplikaci nasadíte na web Azure, aby byla přístupná ze všech zařízení.

  1. Klikněte pravým tlačítkem na projekt AppBackend a vyberte Publikovat.

  2. Jako cíl publikování vyberte Microsoft Azure App Service a pak vyberte \*\*Publikovat. Otevře se okno Vytvořit plán App Service. Tady můžete vytvořit všechny prostředky Azure, které jsou potřeba ke spuštění webové aplikace ASP.NET v Azure.

    The Microsoft Azure App Service tile

  3. V okně Vytvořit plán App Service vyberte váš účet Azure. Vyberte Změnit typ>Webová aplikace. Ponechejte výchozí Název webové aplikace a vyberte Předplatné, Skupinu prostředků a Plán služby App Service.

  4. Vyberte Vytvořit.

  5. Poznamenejte si vlastnost Adresa URL webu v části Souhrn. Tato adresa URL je váš koncový bod back-endu, který použijete později v tomto kurzu.

  6. Vyberte Publikovat.

Průvodce po dokončení publikuje webovou aplikaci ASP.NET do Azure a pak tuto aplikaci otevře ve výchozím prohlížeči. Vaši aplikaci bude možné zobrazit ve službě Azure App Service.

Adresa URL používá název webové aplikace, který jste zadali dříve, s formátem http://< app_name.azurewebsites.net>.

Vytvoření projektu Android

Dalším krokem je aktualizace aplikace pro Android vytvořené v kurzu: Nabízená oznámení do zařízení s Androidem pomocí služby Azure Notification Hubs a služby Firebase Cloud Messaging.

  1. Otevřete soubor, nahraďte res/layout/activity_main.xml následující definice obsahu:

    Tím se přidají ovládací prvky textových polí umožňující přihlášení uživatele. Přidá se také pole pro značku uživatelského jména, která bude součástí oznámení, která odešlete:

    <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"
    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/toggleButtonFCM"
        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/toggleButtonFCM"
        android:layout_centerVertical="true" />
    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOn="FCM on"
        android:textOff="FCM off"
        android:id="@+id/toggleButtonFCM"
        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/toggleButtonFCM"
        android:layout_centerVertical="true" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editTextNotificationMessageTag"
        android:layout_below="@id/toggleButtonFCM"
        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" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/text_hello"
        />
    </RelativeLayout>
    
  2. Otevřete soubor a nahraďte res/values/strings.xmlsend_button definici následujícími řádky, které předefinují řetězec pro send_button ostatní ovládací prvky a přidají řetězce pro ostatní ovládací prvky:

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

    Vaše main_activity.xml grafické rozložení by teď mělo vypadat jako na následujícím obrázku:

    Screenshot of an emulator displaying what the main activity X M L graphical layout will look like.

  3. Vytvořte novou třídu s názvem RegisterClient ve stejném balíčku jako vaše MainActivity třída. Pro soubor s novou třídou použijte následující kód.

    
    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 backendEndpoint) {
            super();
            this.settings = context.getSharedPreferences(PREFS_NAME, 0);
            httpClient =  new DefaultHttpClient();
            Backend_Endpoint = backendEndpoint + "/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", "fcm");
            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;
        }
    }
    

    Tato komponenta implementuje volání REST vyžadovaná ke kontaktování back-endu aplikace, aby se zaregistrovala pro nabízená oznámení. Kromě toho místně ukládá ID registrací vytvořená centrem oznámení, jak je podrobně popsáno v tématu popisujícím registraci z back-endu aplikace. Používá autorizační token uložený v místním úložišti, když kliknete na tlačítko Přihlásit se.

  4. Do třídy MainActivity přidejte pole pro RegisterClient třídu a řetězec pro koncový bod ASP.NET back-endu. Nezapomeňte nahradit <Enter Your Backend Endpoint> skutečným koncovým bodem vašeho back-endu, který jste předtím získali. Například http://mybackend.azurewebsites.net.

    private RegisterClient registerClient;
    private static final String BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
    FirebaseInstanceId fcm;
    String FCM_token = null;
    
  5. V třídě MainActivity v metodě onCreate odeberte nebo okomentujte inicializaci pole hub a volání metody registerWithNotificationHubs. Pak přidejte kód pro inicializaci instance třídy RegisterClient. Metoda by měla obsahovat následující řádky:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        mainActivity = this;
        FirebaseService.createChannelAndHandleNotifications(getApplicationContext());
        fcm = FirebaseInstanceId.getInstance();
        registerClient = new RegisterClient(this, BACKEND_ENDPOINT);
        setContentView(R.layout.activity_main);
    }
    
  6. Přidejte do souboru MainActivity.java následující příkazy import.

    import android.util.Base64;
    import android.view.View;
    import android.widget.EditText;
    
    import android.widget.Button;
    import android.widget.ToggleButton;
    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;
    
    import android.os.AsyncTask;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    
    import com.google.firebase.iid.FirebaseInstanceId;
    import com.google.firebase.iid.InstanceIdResult;
    import com.google.android.gms.tasks.OnSuccessListener;
    import java.util.concurrent.TimeUnit;
    
  7. Nahraďte kód metody onStart následujícím kódem:

    super.onStart();
    Button sendPush = (Button) findViewById(R.id.sendbutton);
    sendPush.setEnabled(false);
    
  8. Pak přidejte následující metody pro zpracování události kliknutí na tlačítko Přihlásit se a odesílání nabízených oznámení.

    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 {
    
                    FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
                        @Override
                        public void onSuccess(InstanceIdResult instanceIdResult) {
                            FCM_token = instanceIdResult.getToken();
                            Log.d(TAG, "FCM Registration Token: " + FCM_token);
                        }
                    });
                    TimeUnit.SECONDS.sleep(1);
                    registerClient.register(FCM_token, 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, "Signed 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", "fcm", "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);
    }
    

    Obslužná login rutina pro tlačítko Přihlásit se vygeneruje základní ověřovací token pomocí vstupního uživatelského jména a hesla (představuje jakýkoli token, který používá schéma ověřování), pak používá RegisterClient k volání back-endu pro registraci.

    Metoda sendPush zavolá back-end za účelem aktivace zabezpečeného oznámení pro uživatele na základě značky uživatele. Systém oznámení platformy, na který metoda sendPush cílí, závisí na předaném řetězci pns.

  9. Do třídy MainActivity přidejte následující metodu DialogNotify.

    protected void DialogNotify(String title, String message)
    {
        AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
        alertDialog.setTitle(title);
        alertDialog.setMessage(message);
        alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();
    }
    
  10. V třídě MainActivity následujícím způsobem aktualizujte metodu sendNotificationButtonOnClick, aby volala metodu sendPush s použitím systému oznámení platformy vybraného uživatelem.

    /**
    * 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.toggleButtonFCM)).isChecked())
        {
            sendPush("fcm", nhMessageTag, nhMessage);
        }
        if (((ToggleButton)findViewById(R.id.toggleButtonAPNS)).isChecked())
        {
            sendPush("apns", nhMessageTag, nhMessage);
        }
    }
    
  11. build.gradle Do souboru za oddíl buildTypes přidejte následující řádekandroid.

    useLibrary 'org.apache.http.legacy'
    
  12. Pokud vaše aplikace cílí na rozhraní API úrovně 28 (Android 9.0) nebo vyšší, zahrňte následující deklaraci do <application> prvku AndroidManifest.xml.

    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />
    
  13. Sestavte projekt.

Otestování aplikace

  1. Pomocí Android Studia spusťte aplikaci na zařízení nebo v emulátoru.

  2. V aplikaci pro Android zadejte uživatelské jméno a heslo. Oba řetězce musí mít stejnou hodnotu a nesmí obsahovat mezery ani speciální znaky.

  3. V aplikaci pro Android klikněte na Přihlásit se. Počkejte na informační zprávu se zprávou s informacemi o přihlášení a registraci. Tím se aktivuje tlačítko Send Notification (Odeslat oznámení).

    Screenshot of an emulator showing what the Notification Hubs Notify Users app looks like after logging in.

  4. Kliknutím na přepínací tlačítko povolte všechny platformy, na kterých jste aplikaci spustili a zaregistrovali uživatele.

  5. Zadejte jméno uživatele, který obdrží zprávu oznámení. Tento uživatel musí mít na cílových zařízení zaregistrovaná oznámení.

  6. Zadejte zprávu, kterou uživatel obdrží jako nabízené oznámení.

  7. Klikněte na Send Notification (Odeslat oznámení). Nabízené oznámení obdrží všechna zařízení, jejichž registrace odpovídá značce uživatelského jména.

Další kroky

V tomto kurzu jste zjistili, jak posílat nabízená oznámení konkrétním uživatelům, k jejichž registracím jsou přidružené značky. V dalším kurzu se dozvíte, jak posílat nabízená oznámení na základě polohy: