Místní oznámení v Xamarin.Forms
Místní oznámení jsou upozornění odesílaná aplikacemi nainstalovanými na mobilním zařízení. Místní oznámení se často používají pro funkce, jako jsou:
- Události kalendáře
- Připomenutí
- Triggery založené na poloze
Každá platforma zpracovává vytváření, zobrazování a spotřebu místních oznámení odlišně. Tento článek vysvětluje, jak vytvořit multiplatformní abstrakci pro odesílání, plánování a přijímání místních oznámení pomocí Xamarin.Forms.
Vytvoření rozhraní pro různé platformy
Aplikace Xamarin.Forms by měla vytvářet a využívat oznámení bez obav související implementace platformy. Následující INotificationManager
rozhraní se implementuje v knihovně sdíleného kódu a definuje rozhraní API pro různé platformy, které může aplikace používat k interakci s oznámeními:
public interface INotificationManager
{
event EventHandler NotificationReceived;
void Initialize();
void SendNotification(string title, string message, DateTime? notifyTime = null);
void ReceiveNotification(string title, string message);
}
Toto rozhraní se implementuje v každém projektu platformy. Tato NotificationReceived
událost umožňuje aplikaci zpracovávat příchozí oznámení. Metoda Initialize
by měla provést jakoukoli logiku nativní platformy potřebnou k přípravě systému oznámení. Metoda SendNotification
by měla odeslat oznámení na volitelném DateTime
místě . Metoda ReceiveNotification
by měla být volána základní platformou při přijetí zprávy.
Využití rozhraní v Xamarin.Forms
Po vytvoření rozhraní je možné ho využívat ve sdíleném Xamarin.Forms projektu, i když implementace platformy ještě nebyly vytvořeny. Ukázková aplikace obsahuje ContentPage
název MainPage.xaml s následujícím obsahem:
<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>
Rozložení obsahuje Label
prvky, které vysvětlují pokyny, a Button
prvky, které po klepnutí odesílají nebo plánují oznámení.
Kód MainPage
třídy zpracovává odesílání a příjem oznámení:
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);
});
}
}
Konstruktor MainPage
třídy používá Xamarin.FormsDependencyService
k načtení instance specifické pro platformu objektu INotificationManager
. Tyto OnSendClick
metody OnScheduleClicked
používají INotificationManager
instanci k odesílání a plánování nových oznámení. Metoda ShowNotification
se volá z obslužné rutiny události připojené k NotificationReceived
události a při vyvolání události vloží na stránku novou Label
.
Obslužná rutina NotificationReceived
události přetypuje argumenty události na NotificationEventArgs
. Tento typ je definován ve sdíleném Xamarin.Forms projektu:
public class NotificationEventArgs : EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
}
Další informace o službě Xamarin.FormsDependencyService
DependencyService naleznete v tématuXamarin.Forms.
Vytvoření implementace rozhraní androidu
Xamarin.Forms Aby aplikace mohla odesílat a přijímat oznámení v Androidu, musí aplikace poskytovat implementaci INotificationManager
rozhraní.
Vytvoření třídy AndroidNotificationManager
Třída AndroidNotificationManager
implementuje INotificationManager
rozhraní:
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
nad oborem názvů registruje implementaci INotificationManager
rozhraní pomocí DependencyService
.
Android umožňuje aplikacím definovat více kanálů pro oznámení. Tato Initialize
metoda vytvoří základní kanál, který ukázková aplikace používá k odesílání oznámení. Metoda SendNotification
definuje logiku specifickou pro platformu potřebnou k vytvoření a odeslání oznámení. Metoda ReceiveNotification
je volána operačním systémem Android při přijetí zprávy a vyvolá obslužnou rutinu události.
Metoda SendNotification
vytvoří místní oznámení okamžitě nebo přesně DateTime
. Oznámení může být naplánováno na přesné DateTime
použití AlarmManager
třídy a oznámení bude přijato objektem, který je odvozen od BroadcastReceiver
třídy:
[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);
}
}
}
Důležité
Oznámení naplánovaná pomocí AlarmManager
třídy ve výchozím nastavení nepřežijí restartování zařízení. Aplikaci ale můžete navrhnout tak, aby automaticky přeplánovala oznámení, pokud se zařízení restartuje. Další informace potřebujete, když se zařízení restartuje v plánovači opakujících se alarmů na developer.android.com a vzorek. Informace o zpracování na pozadí v Androidu najdete v průvodci zpracováním na pozadí na developer.android.com.
Další informace o přijímačích vysílání naleznete v tématu Přijímače vysílání v Xamarin.Android.
Zpracování příchozích oznámení v Androidu
Třída MainActivity
musí detekovat příchozí oznámení a informovat AndroidNotificationManager
instanci. Atribut Activity
třídy MainActivity
by měl obsahovat LaunchMode
hodnotu LaunchMode.SingleTop
:
[Activity(
//...
LaunchMode = LaunchMode.SingleTop]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
// ...
}
Režim SingleTop
zabraňuje spuštění více instancí Activity
v době, kdy je aplikace v popředí. To LaunchMode
nemusí být vhodné pro aplikace, které spouštějí více aktivit ve složitějších scénářích oznámení. Další informace o LaunchMode
výčtových hodnotách naleznete v části Android Activity LaunchMode.
MainActivity
Ve třídě je upravena tak, aby přijímala příchozí oznámení:
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);
}
}
Metoda CreateNotificationFromIntent
extrahuje data oznámení z argumentu intent
a poskytne ji AndroidNotificationManager
k použití ReceiveNotification
metody. Metoda CreateNotificationFromIntent
se volá z OnCreate
metody i OnNewIntent
metody:
- Když aplikace spustí data oznámení,
Intent
data se předají metoděOnCreate
. - Pokud je aplikace již v popředí,
Intent
data budou předána metoděOnNewIntent
.
Android nabízí řadu rozšířených možností pro oznámení. Další informace najdete v tématu Oznámení v Xamarin.Android.
Vytvoření implementace rozhraní pro iOS
Xamarin.Forms Aby aplikace mohla odesílat a přijímat oznámení v iOSu, musí aplikace poskytnout implementaci INotificationManager
.
Vytvoření třídy iOSNotificationManager
Třída iOSNotificationManager
implementuje INotificationManager
rozhraní:
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
nad oborem názvů registruje implementaci INotificationManager
rozhraní pomocí DependencyService
.
V iOSu musíte před pokusem o naplánování oznámení požádat o oprávnění k používání oznámení. Metoda Initialize
požaduje autorizaci pro použití místních oznámení. Metoda SendNotification
definuje logiku potřebnou k vytvoření a odeslání oznámení. Metoda ReceiveNotification
bude volána systémem iOS při přijetí zprávy a vyvolá obslužnou rutinu události.
Poznámka:
Metoda SendNotification
vytvoří místní oznámení okamžitě pomocí objektu UNTimeIntervalNotificationTrigger
nebo přesně DateTime
pomocí objektu UNCalendarNotificationTrigger
.
Zpracování příchozích oznámení v iOSu
V iOSu musíte vytvořit delegáta, který podtřídy UNUserNotificationCenterDelegate
zpracovává příchozí zprávy. Ukázková aplikace definuje iOSNotificationReceiver
třídu:
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);
}
}
Tato třída používá DependencyService
k získání instance iOSNotificationManager
třídy a poskytuje příchozí data oznámení metodě ReceiveNotification
.
Třída AppDelegate
musí při spuštění aplikace zadat iOSNotificationReceiver
objekt jako UNUserNotificationCenter
delegáta. K tomu dochází v FinishedLaunching
metodě:
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 nabízí mnoho pokročilých možností pro oznámení. Další informace najdete v tématu Oznámení v Xamarin.iOS.
Testování aplikace
Jakmile projekty platformy obsahují registrovanou implementaci INotificationManager
rozhraní, můžete aplikaci otestovat na obou platformách. Spusťte aplikaci a kliknutím na tlačítko Vytvořit oznámení vytvořte oznámení.
V Androidu se oznámení zobrazí v oznamovací oblasti. Po klepnutí na oznámení aplikace obdrží oznámení a zobrazí zprávu:
V iOSu se příchozí oznámení automaticky přijímají aplikací, aniž by vyžadovala uživatelský vstup. Aplikace obdrží oznámení a zobrazí zprávu: