Bagikan melalui


Pemberitahuan lokal di Xamarin.Forms

Pemberitahuan lokal adalah pemberitahuan yang dikirim oleh aplikasi yang diinstal pada perangkat seluler. Pemberitahuan lokal sering digunakan untuk fitur seperti:

  • Acara kalender
  • Pengingat
  • Pemicu berbasis lokasi

Setiap platform menangani pembuatan, tampilan, dan konsumsi pemberitahuan lokal secara berbeda. Artikel ini menjelaskan cara membuat abstraksi lintas platform untuk mengirim, menjadwalkan, dan menerima pemberitahuan lokal dengan Xamarin.Forms.

Aplikasi pemberitahuan lokal di iOS dan Android

Membuat antarmuka lintas platform

Aplikasi Xamarin.Forms harus membuat dan mengonsumsi pemberitahuan tanpa khawatir terhadap implementasi platform yang mendasar. Antarmuka berikut INotificationManager diimplementasikan di pustaka kode bersama, dan menentukan API lintas platform yang dapat digunakan aplikasi untuk berinteraksi dengan pemberitahuan:

public interface INotificationManager
{
    event EventHandler NotificationReceived;
    void Initialize();
    void SendNotification(string title, string message, DateTime? notifyTime = null);
    void ReceiveNotification(string title, string message);
}

Antarmuka ini akan diimplementasikan di setiap proyek platform. Kejadian ini NotificationReceived memungkinkan aplikasi untuk menangani pemberitahuan masuk. Metode Initialize ini harus melakukan logika platform asli yang diperlukan untuk menyiapkan sistem pemberitahuan. Metode SendNotification harus mengirim pemberitahuan, pada opsional DateTime. Metode ReceiveNotification harus dipanggil oleh platform yang mendasar ketika pesan diterima.

Mengonsumsi antarmuka di Xamarin.Forms

Setelah antarmuka dibuat, antarmuka dapat dikonsumsi dalam proyek bersama Xamarin.Forms meskipun implementasi platform belum dibuat. Aplikasi sampel berisi yang ContentPage disebut MainPage.xaml dengan konten berikut:

<StackLayout Margin="0,35,0,0"
             x:Name="stackLayout">
    <Label Text="Click the button below to create a local notification."
           TextColor="Red"
           HorizontalOptions="Center"
           VerticalOptions="Start" />
    <Button Text="Create Notification"
            HorizontalOptions="Center"
            VerticalOptions="Start"
            Clicked="OnSendClick" />
    <Label Text="Click the button below to schedule a local notification for in 10 seconds time."
           TextColor="Red"
           HorizontalOptions="Center"
           VerticalOptions="Start" />
    <Button Text="Create Notification"
            HorizontalOptions="Center"
            VerticalOptions="Start"
            Clicked="OnScheduleClick" />
</StackLayout>

Tata letak berisi Label elemen yang menjelaskan instruksi, dan Button elemen yang mengirim atau menjadwalkan pemberitahuan saat diketuk.

Kode MainPage kelas di belakang menangani pengiriman dan penerimaan pemberitahuan:

public partial class MainPage : ContentPage
{
    INotificationManager notificationManager;
    int notificationNumber = 0;

    public MainPage()
    {
        InitializeComponent();

        notificationManager = DependencyService.Get<INotificationManager>();
        notificationManager.NotificationReceived += (sender, eventArgs) =>
        {
            var evtData = (NotificationEventArgs)eventArgs;
            ShowNotification(evtData.Title, evtData.Message);
        };
    }

    void OnSendClick(object sender, EventArgs e)
    {
        notificationNumber++;
        string title = $"Local Notification #{notificationNumber}";
        string message = $"You have now received {notificationNumber} notifications!";
        notificationManager.SendNotification(title, message);
    }

    void OnScheduleClick(object sender, EventArgs e)
    {
        notificationNumber++;
        string title = $"Local Notification #{notificationNumber}";
        string message = $"You have now received {notificationNumber} notifications!";
        notificationManager.SendNotification(title, message, DateTime.Now.AddSeconds(10));
    }

    void ShowNotification(string title, string message)
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            var msg = new Label()
            {
                Text = $"Notification Received:\nTitle: {title}\nMessage: {message}"
            };
            stackLayout.Children.Add(msg);
        });
    }
}

MainPage Konstruktor kelas menggunakan untuk mengambil instans Xamarin.FormsDependencyService khusus platform dari INotificationManager. Metode OnSendClick dan OnScheduleClicked menggunakan INotificationManager instans untuk mengirim dan menjadwalkan pemberitahuan baru. Metode ShowNotification ini dipanggil dari penanganan aktivitas yang dilampirkan ke NotificationReceived peristiwa, dan akan menyisipkan yang baru Label ke halaman saat peristiwa dipanggil.

Penanganan NotificationReceived aktivitas melemparkan argumen peristiwanya ke NotificationEventArgs. Jenis ini didefinisikan dalam proyek bersama Xamarin.Forms :

public class NotificationEventArgs : EventArgs
{
    public string Title { get; set; }
    public string Message { get; set; }
}

Untuk informasi selengkapnya tentang Xamarin.FormsDependencyService, lihat Xamarin.Forms DependencyService.

Membuat implementasi antarmuka Android

Xamarin.Forms Agar aplikasi dapat mengirim dan menerima pemberitahuan di Android, aplikasi harus menyediakan implementasi INotificationManager antarmuka.

Membuat kelas AndroidNotificationManager

Kelas AndroidNotificationManager mengimplementasikan INotificationManager antarmuka:

using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;

[assembly: Dependency(typeof(LocalNotifications.Droid.AndroidNotificationManager))]
namespace LocalNotifications.Droid
{
    public class AndroidNotificationManager : INotificationManager
    {
        const string channelId = "default";
        const string channelName = "Default";
        const string channelDescription = "The default channel for notifications.";

        public const string TitleKey = "title";
        public const string MessageKey = "message";

        bool channelInitialized = false;
        int messageId = 0;
        int pendingIntentId = 0;

        NotificationManager manager;

        public event EventHandler NotificationReceived;

        public static AndroidNotificationManager Instance { get; private set; }

        public AndroidNotificationManager() => Initialize();

        public void Initialize()
        {
            if (Instance == null)
            {
                CreateNotificationChannel();
                Instance = this;
            }
        }

        public void SendNotification(string title, string message, DateTime? notifyTime = null)
        {
            if (!channelInitialized)
            {
                CreateNotificationChannel();
            }

            if (notifyTime != null)
            {
                Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
                intent.PutExtra(TitleKey, title);
                intent.PutExtra(MessageKey, message);

                PendingIntent pendingIntent = PendingIntent.GetBroadcast(AndroidApp.Context, pendingIntentId++, intent, PendingIntentFlags.CancelCurrent);
                long triggerTime = GetNotifyTime(notifyTime.Value);
                AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
                alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent);
            }
            else
            {
                Show(title, message);
            }
        }

        public void ReceiveNotification(string title, string message)
        {
            var args = new NotificationEventArgs()
            {
                Title = title,
                Message = message,
            };
            NotificationReceived?.Invoke(null, args);
        }

        public void Show(string title, string message)
        {
            Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
            intent.PutExtra(TitleKey, title);
            intent.PutExtra(MessageKey, message);

            PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId++, intent, PendingIntentFlags.UpdateCurrent);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
                .SetContentIntent(pendingIntent)
                .SetContentTitle(title)
                .SetContentText(message)
                .SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.xamagonBlue))
                .SetSmallIcon(Resource.Drawable.xamagonBlue)
                .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);

            Notification notification = builder.Build();
            manager.Notify(messageId++, notification);
        }

        void CreateNotificationChannel()
        {
            manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var channelNameJava = new Java.Lang.String(channelName);
                var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
                {
                    Description = channelDescription
                };
                manager.CreateNotificationChannel(channel);
            }

            channelInitialized = true;
        }

        long GetNotifyTime(DateTime notifyTime)
        {
            DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime);
            double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds;
            long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000;
            return utcAlarmTime; // milliseconds
        }
    }
}

Atribut assembly di atas namespace mendaftarkan INotificationManager implementasi antarmuka dengan DependencyService.

Android memungkinkan aplikasi untuk menentukan beberapa saluran untuk pemberitahuan. Metode ini Initialize membuat saluran dasar yang digunakan aplikasi sampel untuk mengirim pemberitahuan. Metode ini SendNotification menentukan logika khusus platform yang diperlukan untuk membuat dan mengirim pemberitahuan. Metode ReceiveNotification ini dipanggil oleh OS Android saat pesan diterima, dan memanggil penanganan aktivitas.

Metode ini SendNotification segera membuat pemberitahuan lokal, atau dengan tepat DateTime. Pemberitahuan dapat dijadwalkan untuk menggunakan AlarmManager kelas yang tepatDateTime, dan pemberitahuan akan diterima oleh objek yang berasal dari BroadcastReceiver kelas:

[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        if (intent?.Extras != null)
        {
            string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
            string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);

            AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
            manager.Show(title, message);
        }
    }
}

Penting

Secara default, pemberitahuan yang dijadwalkan AlarmManager menggunakan kelas tidak akan bertahan dari mulai ulang perangkat. Namun, Anda dapat merancang aplikasi untuk menjadwalkan ulang pemberitahuan secara otomatis jika perangkat dimulai ulang. Untuk informasi selengkapnya, lihat Memulai alarm saat perangkat dimulai ulang di Menjadwalkan alarm berulang di developer.android.com. Untuk informasi tentang pemrosesan latar belakang di Android, lihat Panduan pemrosesan latar belakang di developer.android.com.

Untuk informasi selengkapnya tentang penerima siaran, lihat Penerima Siaran di Xamarin.Android.

Menangani pemberitahuan masuk di Android

Kelas MainActivity harus mendeteksi pemberitahuan masuk dan memberi tahu AndroidNotificationManager instans. Atribut Activity pada MainActivity kelas harus menentukan LaunchMode nilai LaunchMode.SingleTop:

[Activity(
        //...
        LaunchMode = LaunchMode.SingleTop]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        // ...
    }

Mode ini SingleTop mencegah beberapa instans Activity dimulai saat aplikasi berada di latar depan. Ini LaunchMode mungkin tidak sesuai untuk aplikasi yang meluncurkan beberapa aktivitas dalam skenario pemberitahuan yang lebih kompleks. Untuk informasi selengkapnya tentang LaunchMode nilai enumerasi, lihat LaunchMode Aktivitas Android.

MainActivity Di kelas dimodifikasi untuk menerima pemberitahuan masuk:

protected override void OnCreate(Bundle savedInstanceState)
{
    // ...

    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    LoadApplication(new App());
    CreateNotificationFromIntent(Intent);
}

protected override void OnNewIntent(Intent intent)
{
    CreateNotificationFromIntent(intent);
}

void CreateNotificationFromIntent(Intent intent)
{
    if (intent?.Extras != null)
    {
        string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
        string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
        DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
    }
}

Metode ini CreateNotificationFromIntent mengekstrak data pemberitahuan dari intent argumen dan menyediakannya ke AndroidNotificationManager menggunakan ReceiveNotification metode . Metode CreateNotificationFromIntent ini dipanggil dari OnCreate metode dan OnNewIntent metode :

  • Ketika aplikasi dimulai oleh data pemberitahuan, Intent data akan diteruskan ke OnCreate metode .
  • Jika aplikasi sudah berada di latar depan, Intent data akan diteruskan ke OnNewIntent metode .

Android menawarkan banyak opsi tingkat lanjut untuk pemberitahuan. Untuk informasi selengkapnya, lihat Pemberitahuan di Xamarin.Android.

Membuat implementasi antarmuka iOS

Xamarin.Forms Agar aplikasi mengirim dan menerima pemberitahuan di iOS, aplikasi harus memberikan implementasi dari INotificationManager.

Membuat kelas iOSNotificationManager

Kelas iOSNotificationManager mengimplementasikan INotificationManager antarmuka:

using System;
using Foundation;
using UserNotifications;
using Xamarin.Forms;

[assembly: Dependency(typeof(LocalNotifications.iOS.iOSNotificationManager))]
namespace LocalNotifications.iOS
{
    public class iOSNotificationManager : INotificationManager
    {
        int messageId = 0;
        bool hasNotificationsPermission;
        public event EventHandler NotificationReceived;

        public void Initialize()
        {
            // request the permission to use local notifications
            UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert, (approved, err) =>
            {
                hasNotificationsPermission = approved;
            });
        }

        public void SendNotification(string title, string message, DateTime? notifyTime = null)
        {
            // EARLY OUT: app doesn't have permissions
            if (!hasNotificationsPermission)
            {
                return;
            }

            messageId++;

            var content = new UNMutableNotificationContent()
            {
                Title = title,
                Subtitle = "",
                Body = message,
                Badge = 1
            };            

            UNNotificationTrigger trigger;
            if (notifyTime != null)
            {
                // Create a calendar-based trigger.
                trigger = UNCalendarNotificationTrigger.CreateTrigger(GetNSDateComponents(notifyTime.Value), false);
            }
            else
            {
                // Create a time-based trigger, interval is in seconds and must be greater than 0.
                trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false);
            }                      

            var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger);
            UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
            {
                if (err != null)
                {
                    throw new Exception($"Failed to schedule notification: {err}");
                }
            });
        }

        public void ReceiveNotification(string title, string message)
        {
            var args = new NotificationEventArgs()
            {
                Title = title,
                Message = message
            };
            NotificationReceived?.Invoke(null, args);
        }

        NSDateComponents GetNSDateComponents(DateTime dateTime)
        {
            return new NSDateComponents
            {
                Month = dateTime.Month,
                Day = dateTime.Day,
                Year = dateTime.Year,
                Hour = dateTime.Hour,
                Minute = dateTime.Minute,
                Second = dateTime.Second
            };
        }
    }
}

Atribut assembly di atas namespace mendaftarkan INotificationManager implementasi antarmuka dengan DependencyService.

Di iOS, Anda harus meminta izin untuk menggunakan pemberitahuan sebelum mencoba menjadwalkan pemberitahuan. Metode ini Initialize meminta otorisasi untuk menggunakan pemberitahuan lokal. Metode menentukan SendNotification logika yang diperlukan untuk membuat dan mengirim pemberitahuan. Metode ReceiveNotification ini akan dipanggil oleh iOS saat pesan diterima, dan memanggil penanganan aktivitas.

Catatan

Metode ini SendNotification segera membuat pemberitahuan lokal, menggunakan UNTimeIntervalNotificationTrigger objek, atau secara tepat DateTime menggunakan UNCalendarNotificationTrigger objek.

Menangani pemberitahuan masuk di iOS

Di iOS, Anda harus membuat delegasi yang subkelas UNUserNotificationCenterDelegate untuk menangani pesan masuk. Aplikasi sampel mendefinisikan iOSNotificationReceiver kelas:

public class iOSNotificationReceiver : UNUserNotificationCenterDelegate
{
    public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
    {
        ProcessNotification(notification);
        completionHandler(UNNotificationPresentationOptions.Alert);
    }

    void ProcessNotification(UNNotification notification)
    {
        string title = notification.Request.Content.Title;
        string message = notification.Request.Content.Body;

        DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
    }    
}

Kelas ini menggunakan untuk mendapatkan instans DependencyServiceiOSNotificationManager kelas dan menyediakan data pemberitahuan masuk ke ReceiveNotification metode .

Kelas AppDelegate harus menentukan iOSNotificationReceiver objek sebagai UNUserNotificationCenter delegasi selama pengaktifan aplikasi. Ini terjadi dalam FinishedLaunching metode :

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();

    UNUserNotificationCenter.Current.Delegate = new iOSNotificationReceiver();

    LoadApplication(new App());
    return base.FinishedLaunching(app, options);
}

iOS menawarkan banyak opsi tingkat lanjut untuk pemberitahuan. Untuk informasi selengkapnya, lihat Pemberitahuan di Xamarin.iOS.

Uji aplikasi

Setelah proyek platform berisi implementasi INotificationManager antarmuka yang terdaftar, aplikasi dapat diuji di kedua platform. Jalankan aplikasi dan klik salah satu tombol Buat Pemberitahuan untuk membuat pemberitahuan.

Di Android, pemberitahuan akan muncul di area pemberitahuan. Saat pemberitahuan diketuk, aplikasi menerima pemberitahuan dan menampilkan pesan:

Pemberitahuan lokal di Android

Di iOS, pemberitahuan masuk secara otomatis diterima oleh aplikasi tanpa memerlukan input pengguna. Aplikasi menerima pemberitahuan dan menampilkan pesan:

Pemberitahuan lokal di iOS