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 najdete v tématu Spuštění alarmu, když se zařízení restartuje v plánu opakování alarmů na developer.android.com. 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: