البرنامج التعليمي: إرسال إشعارات الدفع إلى تطبيقات Android باستخدام Azure Notification Hubs

إشعار

للحصول على معلومات حول إيقاف Firebase Cloud Messaging وخطوات الترحيل، راجع ترحيل Google Firebase Cloud Messaging.

يوضح لك هذا البرنامج التعليمي طريقة استخدام Azure Notification Hubs لإرسال إشعارات منبثقة إلى مُستخدم تطبيق محدَّد على جهاز محدَّد. تُستخدم خلفية واجهة برمجة تطبيقات الويب لـ ASP.NET لمصادقة العملاء وتوليد الإشعارات، كما هو موضح في المقالة التوجيهية Registering from your app backend. يقوم هذا البرنامج التعليمي على أساس مركز الإعلام الذي أُنشئ في البرنامج التعليمي: دفع الإشعارات إلى أجهزة Android باستخدام Azure Notification Hubs وFirebaseFirebase Cloud Messaging.

في هذا البرنامج التعليمي، نُفذت الخطوات التالية:

  • قم بإنشاء مشروع واجهة برمجة تطبيقات الويب للواجهة الخلفية التي تقوم بمصادقة المستخدمين.
  • قم بتحديث تطبيق نظام تشغيل Android.
  • اختبار التطبيق

المتطلبات الأساسية

إكمال البرنامج التعليمي: دفع الإشعارات إلى أجهزة Android استخدام Azure Notification Hubs وGoogle Firebase Cloud Messaging قبل عرض هذا البرنامج التعليمي.

إنشاء مشروع WebAPI

تناقش الفصول التالية إنشاء خلفية WebAPI ASP.NET جديدة. ولهذه العملية ثلاثة أغراض رئيسية:

  • مصادقة العملاء: يمكنك إضافة معالج رسالة لمصادقة طلبات العميل وإقران المستخدم مع الطلب.
  • التسجيل للحصول على إعلامات باستخدام واجهة برمجة تطبيقات الويب الخلفية: إضافة وحدة تحكم لمعالجة تسجيلات جديدة لجهاز عميل لتلقي الإشعارات. تتم إضافة اسم المستخدم المصادق عليه تلقائياً إلى التسجيل كعلامة.
  • إرسال إشعارات للعملاء:يمكنك إضافة وحدة تحكم لتوفير طريقة للمستخدمين لتشغيل دفعة آمنة للأجهزة والعملاء المقترنين بالعلامة.

أنشئ الواجهة الخلفية لواجهة برمجة تطبيقات الويب ASP.NET Core 6.0 الجديدة عن طريق القيام بالإجراءات التالية:

للتحقق، شغل Visual Studio. في قائمة "Tools"، حدد "Extensions and Updates". ابحث عن نسخة NuGet Package Manager من إصدار Visual Studio، وتأكد من حصولك على أحدث إصدار. إذا لم يكن الإصدار الخاص بك هو أحدث إصدار، فقم بإلغاء تثبيته، ثم أعد تثبيت NuGet Package Manager.

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

إشعار

تأكد من تثبيت Visual Studio Azure SDK لنشر موقع ويب.

  1. شغل Visual Studio أو Visual Studio Express.

  2. حدد "Server Explorer"، ثم سجل الدخول إلى حساب Azure. لإنشاء موارد موقع ويب على حسابك، يجب تسجيل الدخول.

  3. في قائمة File في Visual Studio، حدد New>Project.

  4. أدخل Web API في مربع البحث.

  5. حدد قالب مشروع ASP.NET Core Web API وحدد Next.

  6. في مربع الحوار Configure your new project، قم بتسمية المشروع AppBackend وحدد Next.

  7. في مربع الحوار Additional information:

    • تأكد من أن Framework هو .NET 6.0 (دعم طويل الأجل).
    • تأكد من تحديد مربع الاختيار (قم بإلغاء تحديده لاستخدام الحد الأدنى من واجهات برمجة التطبيقات).
    • قم بإلغاء تحديد Enable OpenAPI support.
    • حدد إنشاء.

قم بإزالة ملفات قالب WeatherForecast

  1. قم بإزالة نماذج ملفات WeatherForecast.cs وControllers/WeatherForecastController.cs من مشروع AppBackend الجديد.
  2. افتح Properties\launchSettings.json.
  3. غيّر خصائص launchUrl من weatherforcast إلى appbackend.

في نافذة Configure Microsoft Azure Web App، حدد اشتراك ثم في قائمة App Service plan، نفذ أي من الإجراءات التالية:

  • حدد خطة خدمة تطبيق Azure التي قمت بإنشائها بالفعل.
  • حدد Create a new app service plan ثم قم بإنشاء خطة.

لا تحتاج إلى قاعدة بيانات لهذا البرنامج التعليمي. بعد تحديد خطة خدمة التطبيق، حدد "OK" لإنشاء المشروع.

The Configure Microsoft Azure Web App window

إذا لم تشاهد هذه الصفحة لتكوين خطة خدمة التطبيق، فتابع البرنامج التعليمي. يمكنك تكوينه أثناء نشر التطبيق لاحقاً.

مصادقة العملاء في واجهة برمجة تطبيقات الويب الخلفية

في هذا المقطع، يمكنك إنشاء فئة معالج رسالة جديدة تسمى AuthenticationTestHandler للواجهة الخلفية الجديدة. يتم اشتقاق هذه الفئة من DelegatingHandler وإضافتها كمعالج رسالة بحيث يمكن معالجة جميع الطلبات التي تأتي في الخلفية.

  1. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق مشروع AppBackend، وحدد "Add"، ثم حدد "Class".

  2. اسم فئة جديدة AuthenticationTestHandler.cs، ثم حدد "Add" لإنشاء الفئة. هذه الفئة تصادق المستخدمين باستخدام المصادقة الأساسية للبساطة. يمكن للتطبيق استخدام أي نظام مصادقة.

  3. في AuthenticationTestHandler.cs، إضافة using العبارات التالية:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. في AuthenticationTestHandler.cs استبدال تعريف AuthenticationTestHandler الفئة مع التعليمات البرمجية التالية:

    المعالج يسمح بالطلب عند تحقق الشروط الثلاثة التالية:

    • يتضمن الطلب عنوان ⁧التصريح.⁩
    • يستخدم الطلب المصادقة الأساسية.
    • سلسلة اسم المستخدم وسلسلة كلمة المرور هي نفس السلسلة.

    وإلا، يتم رفض الطلب. هذه المصادقة ليست مصادقة حقيقية ونهج التصريح. إنها مجرد مثال بسيط لهذا البرنامج التعليمي.

    إذا تمت مصادقة رسالة الطلب ومنح تصريح من قبل AuthenticationTestHandler، يتم إرفاق مستخدم المصادقة الأساسي بالطلب الحالي على HttpContext. سيتم استخدام معلومات المستخدم في HttpContext بواسطة وحدة تحكم أخرى (RegisterController) لاحقاً لإضافة طلب إلى طلب تسجيل الإخطار.

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

    إشعار

    ملاحظة الأمان: AuthenticationTestHandler لا توفر الفئة المصادقة الحقيقية. يتم استخدامه فقط لمحاكاة المصادقة الأساسية كما أنه غير آمن. يجب تطبيق آلية مصادقة آمنة في تطبيقات الإنتاج والخدمات.

  5. لتسجيل معالج الرسالة، أضف التعليمة البرمجية التالية في نهاية الطريقة Register في ملف Program.cs :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. احفظ تغييراتك.

التسجيل للحصول على الإشعارات باستخدام الخلفية WebAPI

في هذا القسم، يمكنك إضافة وحدة تحكم جديدة إلى الواجهة الخلفية لواجهة برمجة تطبيقات الويب للتعامل مع طلبات تسجيل مستخدم وجهاز للإخطارات باستخدام مكتبة العميل لمراكز الإشعارات. تضيف وحدة التحكم علامة مستخدم للمستخدم الذي تمت مصادقته وتعلق عليه HttpContext بواسطة AuthenticationTestHandler. العلامة لها تنسيق السلسلة، "username:<actual username>".

  1. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق مشروع AppBackend ثم حدد "Manage NuGet Packages".

  2. في الجزء الأيمن، حدد "Online" ثم، في مربع "Search"، اكتب Microsoft.Azure.NotificationHubs.

  3. في قائمة النتائج، حدد "Microsoft Azure Notification Hubs"، ثم حدد "Install". أكمل التثبيت، ثم أغلق نافذة NuGet Package Manager.

    يضيف هذا الإجراء مرجعاً إلى SDK مراكز إشعار Azure باستخدام حزمة Microsoft.Azure.Notification Hubs NuGet.

  4. إنشاء ملف فئة جديد يمثل الاتصال بمركز الإشعارات المستخدم لإرسال الإشعارات. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق المجلد "Models"، وحدد "Add"، ثم حدد "Class". اختر اسم الفئة الجديدة إشعارات.cs، ثم حدد "Add" لإنشاء الفئة.

    The Add New Item window

  5. في إشعارات.cs، أضف العبارة التالية using في أعلى الملف:

    using Microsoft.Azure.NotificationHubs;
    
  6. استبدال ⁧Notifications⁩ تعريف الفئة مع التعليمات البرمجية التالية، واستبدال العنصرين النائبين مع سلسلة الاتصال (مع الوصول الكامل) لمركز الإشعار واسم لوحة الوصل (متوفر في ⁧بوابة Azure⁩):⁧

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

    هام

    أدخل الاسم و DefaultFullSharedAccessSignature لوحة الوصل قبل المتابعة.

  7. بعد ذلك، إنشاء وحدة تحكم جديدة تسمى RegisterController. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق المجلد وحدات التحكم، وحدد "Add"، ثم حدد "Controller".

  8. حدد API Controller - Empty، ثم حدد Add.

  9. في المربع "Controller name" اكتب RegisterController لتسمية الفئة الجديدة ثم حدد "Add".

    The Add Controller window.

  10. في سجلController.cs، إضافة using العبارات التالية:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. إضافة التعليمات البرمجية التالية داخل RegisterController تعريف الفئة. في هذه التعليمة البرمجية، يمكنك إضافة علامة مستخدم للمستخدم المرفق بـ HttpContext. تمت مصادقة المستخدم وإرفاق HttpContext بواسطة عامل تصفية الرسالة التي قمت AuthenticationTestHandler بإضافتها. يمكنك أيضاً إضافة تدقيقات اختيارية للتحقق من أن المستخدم لديه حقوق التسجيل للعلامات المطلوبة.

    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. احفظ تغييراتك.

إرسال إشعارات من الواجهة الخلفية لواجهة برمجة تطبيقات الويب

في هذا الفصل، يمكنك إضافة وحدة تحكم جديدة تعرض طريقة للأجهزة العميلة لإرسال إشعار. يستند الإشعار إلى علامة اسم المستخدم الذي يستخدم لوحة إشعارات Azure.NET مكتبة في الخلفية WebAPI ASP.NET.

  1. إنشاء وحدة تحكم جديدة أخرى تسمى NotificationsController بنفس الطريقة التي قمت بإنشائها RegisterController في المقطع السابق.

  2. في NotificationsController.cs، إضافة using العبارات التالية:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. إضافة الأسلوب التالي إلى NotificationsController الفئة:

    يرسل هذا الرمز نوع إشعار يستند إلى مؤشر خدمة إشعار المنصة pns. يتم استخدام قيمة to_tag لتعيين علامة اسم المستخدم على الرسالة. يجب أن تتطابق هذه العلامة مع علامة اسم مستخدم لتسجيل مركز إشعار نشط. يتم سحب رسالة الإعلام من نص طلب POST وتنسيقها لخدمة إشعار المنصة المستهدف.

    بناءً على خدمة إشعار المنصة التي تستخدمها الأجهزة المعتمدة لتلقي الإشعارات، يتم دعم الإشعارات بمجموعة متنوعة من التنسيقات. على سبيل المثال، على أجهزة الويندوز، قد تستخدم إشعاراً منبثقاً مع WNSغير معتمد مباشرة من قِبَل خدمة إشعار المنصة أخرى. في مثل هذه الحالة، تحتاج الخلفية لتنسيق الإشعار في إشعار معتمد لخدمة إشعار المنصة من الأجهزة التي تخطط لدعمها. ثم استخدم واجهة تطبيق البرامج للإرسال المناسب على الفئة NotificationHubClient class.

    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. لتشغيل التطبيق والتأكد من دقة عملك حتى الآن، اختر المفتاح ⁧⁩F5.⁧⁩ يفتح التطبيق متصفح ويب، ويتم عرضه على الصفحة الرئيسية ASP.NET.

نشر الخلفية WebAPI الجديدة

بعد ذلك، يمكنك نشر التطبيق على موقع Azure على الويب لجعله متاحاً من جميع الأجهزة.

  1. انقر بزر الماوس الأيمن فوق مشروع AppBackend، ثم حدد "Publish".

  2. حدد "Microsoft Azure App Service" كهدف للنشر، ثم حدد نشر**. يتم فتح نافذة إنشاء خدمة التطبيقات. هنا يمكنك إنشاء جميع الموارد Azure اللازمة لتشغيل التطبيق ASP.NET ويب في Azure.

    The Microsoft Azure App Service tile

  3. في نافذة "Create App Service"، حدد حساب Azure. حدد "Change Type">"Web App". احتفظ باسم تطبيق ويبالافتراضي، ثم حدد "Subscription"، و"Resource Group"، و"App Service Plan".

  4. حدد إنشاء.

  5. دون ملاحظة عن خاصية URL للموقع في قسم الملخص. عنوان URL هذا هو نقطة النهاية الخلفية في وقت لاحق في البرنامج التعليمي.

  6. حدد نشر.

بعد الانتهاء من المعالج، فإنه ينشر تطبيق الويب ASP.NET إلى Azure ثم يفتح التطبيق في المستعرض الافتراضي. تطبيقك قابل للعرض في خدمات تطبيقات Azure.

يستخدم عنوان URL اسم تطبيق الويب الذي حددته مسبقاً، بالتنسيق http://<app_name>.azurewebsites.net.

إنشاء مشروع الـ Android

تتمثل الخطوة التالية في تحديث تطبيق الـ Android الذي أُنشئ في البرنامج التعليمي: دفع الإشعارات إلى أجهزة Android باستخدام Azure Notification Hubs وFirebase Cloud Messaging.

  1. افتح res/layout/activity_main.xml ملفك واستبدل تعريفات المحتوى التالية:

    يضيف هذا عناصر تحكم EditText جديدة لتسجيل الدخول كمستخدم. كما يضاف حقل لعلامة اسم المستخدم التي ستشكل جزءاً من الإشعارات التي تُرسل:

    <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. فتح res/values/strings.xml الملف الخاص واستبدال send_button التعريف بالخطوط التالية التي تعيد تعريف السلسلة send_button لعناصر التحكم الأخرى وتضيف سلاسل لها:

    <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>
    

    main_activity.xmlيجب أن يبدو التخطيط الرسومي الآن كالصورة التالية:

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

  3. إنشاء فئة جديدة مسماة RegisterClient في نفس تسمية MainActivity حزمتك. استخدم التعليمات البرمجية الواردة أدناه لملف الفئة الجديدة.

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

    ينفذ هذا المكون استدعاءات REST المطلوبة للاتصال بالواجهة الخلفية للتطبيق للتسجيل للحصول على إشعارات الدفع. كما أنه يخزن محلياً registrationIds المنشأة من مركز الإعلام كما هو مفصل في Registering from your app backend. يستخدم رمز تخويل مخزن في التخزين المحلي عند تحديد الزر تسجيل الدخول.

  4. في MainActivity فئتك، وإضافة حقل RegisterClient للفئة وسلسلة لنقطة النهاية الخلفية لـ ASP.NET. التأكد من الاستبدال <Enter Your Backend Endpoint> بنقطة النهاية الخلفية الفعلية التي حُصل عليها مسبقا. على سبيل المثال، http://mybackend.azurewebsites.net

    private RegisterClient registerClient;
    private static final String BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
    FirebaseInstanceId fcm;
    String FCM_token = null;
    
  5. في MainActivity فئتك الخاصة، في onCreate الأسلوب، إزالة أو التعليق خارج تهيئة hub الحقل و الاستدعاء إلى registerWithNotificationHubs الأسلوب. ثم إضافة تعليمات برمجية لتهيئة مثيل RegisterClient للفئة. يجب أن يحتوي الأسلوب على الأسطر التالية:

    @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. إضافة العبارات التالية import إلى الملف MainActivity.java.

    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. استبدل التعليمات البرمجية على أسلوب أون ستارت بالتعليمات البرمجية التالية:

    super.onStart();
    Button sendPush = (Button) findViewById(R.id.sendbutton);
    sendPush.setEnabled(false);
    
  8. ثم، إضافة الأساليب التالية لمعالجة الحدث تحديد زر تسجيل الدخول وإرسال إشعارات الدفع.

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

    يولد loginمعالج زر تسجيل الدخول رمز مصادقة أساسي لاستخدامه على اسم المستخدم وكلمة المرور (حيث يمثل أي رمز مميز يستخدم نظام المصادقة الخاص بك)، ثم يستخدم لاستدعاء RegisterClient الخلفية للتسجيل.

    sendPushيستدعي الأسلوب الخلفية لتشغيل إشعار آمن للمستخدم استناداً إلى علامة المستخدم. خدمة إعلام النظام الأساسي التي تستهدفها sendPush تعتمد على السلسلة pns التي مُررت.

  9. إضافة DialogNotify الأسلوب التالي إلى MainActivity الفئة.

    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. في MainActivity فئتك، تحديث sendNotificationButtonOnClick الأسلوب لاستدعاء sendPush الأسلوب باستخدام خدمات إشعار النظام الأساسي المحددة للمستخدمين كما يلي.

    /**
    * 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 الملف، إضافة السطر التالي إلى android المقطع بعد buildTypes المقطع.

    useLibrary 'org.apache.http.legacy'
    
  12. إذا كان تطبيقك يستهدف مستوى API 28 (Android 9.0) أو أعلى، فيجب تضمين الإعلان التالي ضمن <application> عنصر AndroidManifest.xml .

    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />
    
  13. أنشئ المشروع.

اختبار التطبيق

  1. قم بتشغيل التطبيق على جهاز أو محاكي باستخدام Android Studio.

  2. في تطبيق Android، قم بإدخال اسم مستخدم وكلمة مرور. يجب أن يكونا كلاهما من نفس قيمة السلسلة ويجب ألا تحتوي على مسافات أو أحرف خاصة.

  3. في تطبيق Android، تحديد تسجيل الدخول. انتظار رسالة التنبيه الطافي التي تبين تسجيل الدخول والتسجيل. إن هذا يُتيح زر إرسال إشعار.

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

  4. انقر على أزرار التبديل لتمكين كافة الأنظمة الأساسية حيث قمت بتشغيل التطبيق وتسجيل مستخدم.

  5. أدخل اسم المستخدم الذي يتلقى رسالة الإشعار. يجب تسجيل ذلك المستخدم للإشعارات على الأجهزة المستهدفة.

  6. إدخال رسالة للمستخدم لتلقيها كرسالة إشعار منبثق.

  7. تحديد إرسال إشعار. يتلقى كل جهاز يحتوي على تسجيل باستخدام وسم اسم المستخدم مطابقة الإشعار المنبثق.

الخطوات التالية

في هذا البرنامج التعليمي، تعلمت كيفية دفع الإشعارات إلى مستخدمين محددين لديهم علامات مرتبطة بتسجيلاتهم. لمعرفة طريقة عرض الإعلامات إلى أجهزة Android معينة، الدخول إلى البرنامج التعليمي التالي: