提交
This commit is contained in:
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3040eaa3b547042fd9ee979fee321768
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows applying a rich notification style to a notification.
|
||||
/// </summary>
|
||||
public enum NotificationStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the default style.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Generate a large-format notification centered around an image.
|
||||
/// </summary>
|
||||
BigPictureStyle = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Generate a large-format notification that includes a lot of text.
|
||||
/// </summary>
|
||||
BigTextStyle = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows applying an alert behaviour to grouped notifications.
|
||||
/// </summary>
|
||||
public enum GroupAlertBehaviours
|
||||
{
|
||||
/// <summary>
|
||||
/// All notifications in a group with sound or vibration will make sound or vibrate, so this notification will not be muted when it is in a group.
|
||||
/// </summary>
|
||||
GroupAlertAll = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The summary notification in a group will be silenced (no sound or vibration) even if they would otherwise make sound or vibrate.
|
||||
/// Use this to mute this notification if this notification is a group summary.
|
||||
/// </summary>
|
||||
GroupAlertSummary = 1,
|
||||
|
||||
/// <summary>
|
||||
/// All children notification in a group will be silenced (no sound or vibration) even if they would otherwise make sound or vibrate.
|
||||
/// Use this to mute this notification if this notification is a group child. This must be set on all children notifications you want to mute.
|
||||
/// </summary>
|
||||
GroupAlertChildren = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data for setting up the big picture style notification.
|
||||
/// Properties that are not available in devices API level are ignored. See Android documentation for availibility.
|
||||
/// <see href="https://developer.android.com/reference/android/app/Notification.BigPictureStyle"/>
|
||||
/// </summary>
|
||||
public struct BigPictureStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// The override for large icon (requirements are the same).
|
||||
/// </summary>
|
||||
/// <see cref="AndroidNotification.LargeIcon"/>
|
||||
public string LargeIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The picture to be displayed.
|
||||
/// Can be resource name (like icon), file path or an URI supported by Android.
|
||||
/// </summary>
|
||||
public string Picture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The content title to be displayed in the notification.
|
||||
/// </summary>
|
||||
public string ContentTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The content description to set.
|
||||
/// </summary>
|
||||
public string ContentDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The summary text to be shown.
|
||||
/// </summary>
|
||||
public string SummaryText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show big picture in place of large icon when collapsed.
|
||||
/// </summary>
|
||||
public bool ShowWhenCollapsed { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AndroidNotification is used schedule a local notification, which includes the content of the notification.
|
||||
/// </summary>
|
||||
public struct AndroidNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Notification title.
|
||||
/// Set the first line of text in the notification.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notification body.
|
||||
/// Set the second line of text in the notification.
|
||||
/// </summary>
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notification small icon.
|
||||
/// It will be used to represent the notification in the status bar and content view (unless overridden there by a large icon)
|
||||
/// The icon has to be registered in Notification Settings or a PNG file has to be placed in the `res/drawable` folder of the Android library plugin
|
||||
/// and it's name has to be specified without the extension.
|
||||
/// Alternatively it can also be URI supported by the OS.
|
||||
/// </summary>
|
||||
public string SmallIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date and time when the notification should be delivered.
|
||||
/// </summary>
|
||||
public DateTime FireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The notification will be be repeated on every specified time interval.
|
||||
/// Do not set for one time notifications.
|
||||
/// </summary>
|
||||
public TimeSpan? RepeatInterval
|
||||
{
|
||||
get { return m_RepeatInterval; }
|
||||
set { m_RepeatInterval = value.HasValue ? value.Value : (-1L).ToTimeSpan(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notification large icon.
|
||||
/// Add a large icon to the notification content view. This image will be shown on the left of the notification view in place of the small icon (which will be placed in a small badge atop the large icon).
|
||||
/// The icon has to be registered in Notification Settings or a PNG file has to be placed in the `res/drawable` folder of the Android library plugin
|
||||
/// and it's name has to be specified without the extension.
|
||||
/// Alternatively it can be a file path or system supported URI.
|
||||
/// </summary>
|
||||
public string LargeIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Apply a custom style to the notification.
|
||||
/// Currently only BigPicture and BigText styles are supported.
|
||||
/// </summary>
|
||||
public NotificationStyle Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Accent color to be applied by the standard style templates when presenting this notification.
|
||||
/// The template design constructs a colorful header image by overlaying the icon image (stenciled in white) atop a field of this color. Alpha components are ignored.
|
||||
/// </summary>
|
||||
public Color? Color
|
||||
{
|
||||
get { return m_Color; }
|
||||
set { m_Color = value.HasValue ? value.Value : new Color(0, 0, 0, 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number of items this notification represents.
|
||||
/// Is displayed as a badge count on the notification icon if the launcher supports this behavior.
|
||||
/// </summary>
|
||||
public int Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This notification will automatically be dismissed when the user touches it.
|
||||
/// By default this behavior is turned off.
|
||||
/// </summary>
|
||||
public bool ShouldAutoCancel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show the notification time field as a stopwatch instead of a timestamp.
|
||||
/// </summary>
|
||||
public bool UsesStopwatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///Set this property for the notification to be made part of a group of notifications sharing the same key.
|
||||
/// Grouped notifications may display in a cluster or stack on devices which support such rendering.
|
||||
/// Only available on Android 7.0 (API level 24) and above.
|
||||
/// </summary>
|
||||
public string Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this notification to be the group summary for a group of notifications. Requires the 'Group' property to also be set.
|
||||
/// Grouped notifications may display in a cluster or stack on devices which support such rendering.
|
||||
/// Only available on Android 7.0 (API level 24) and above.
|
||||
/// </summary>
|
||||
public bool GroupSummary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the group alert behavior for this notification. Set this property to mute this notification if alerts for this notification's group should be handled by a different notification.
|
||||
/// This is only applicable for notifications that belong to a group. This must be set on all notifications you want to mute.
|
||||
/// Only available on Android 8.0 (API level 26) and above.
|
||||
/// </summary>
|
||||
public GroupAlertBehaviours GroupAlertBehaviour { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sort key will be used to order this notification among other notifications from the same package.
|
||||
/// Notifications will be sorted lexicographically using this value.
|
||||
/// </summary>
|
||||
public string SortKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use this to save arbitrary string data related to the notification.
|
||||
/// </summary>
|
||||
public string IntentData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable it to show a timestamp on the notification when it's delivered, unless the "CustomTimestamp" property is set "FireTime" will be shown.
|
||||
/// </summary>
|
||||
public bool ShowTimestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set this to show custom date instead of the notification's "FireTime" as the notification's timestamp'.
|
||||
/// </summary>
|
||||
public DateTime CustomTimestamp
|
||||
{
|
||||
get { return m_CustomTimestamp; }
|
||||
set
|
||||
{
|
||||
ShowCustomTimestamp = true;
|
||||
m_CustomTimestamp = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set this notification to be shown when app is in the foreground (default: true).
|
||||
/// </summary>
|
||||
public bool ShowInForeground
|
||||
{
|
||||
get => !m_SilentInForeground;
|
||||
set => m_SilentInForeground = !value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The necessary properties for big picture style notification.
|
||||
/// For convenience, assigning this property will also set the Style property.
|
||||
/// </summary>
|
||||
public BigPictureStyle? BigPicture
|
||||
{
|
||||
get { return m_BigPictureStyle; }
|
||||
set
|
||||
{
|
||||
m_BigPictureStyle = value;
|
||||
if (m_BigPictureStyle.HasValue && Style == NotificationStyle.None)
|
||||
Style = NotificationStyle.BigPictureStyle;
|
||||
else if (!m_BigPictureStyle.HasValue && Style == NotificationStyle.BigPictureStyle)
|
||||
Style = NotificationStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ShowCustomTimestamp { get; set; }
|
||||
|
||||
private Color m_Color;
|
||||
private TimeSpan m_RepeatInterval;
|
||||
private DateTime m_CustomTimestamp;
|
||||
private bool m_SilentInForeground;
|
||||
private BigPictureStyle? m_BigPictureStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Create a notification struct with all optional fields set to default values.
|
||||
/// </summary>
|
||||
/// <param name="title">Notification title</param>
|
||||
/// <param name="text">Text to show on notification</param>
|
||||
/// <param name="fireTime">Date and time when to show, can be DateTime.Now to show right away</param>
|
||||
public AndroidNotification(string title, string text, DateTime fireTime)
|
||||
{
|
||||
Title = title;
|
||||
Text = text;
|
||||
FireTime = fireTime;
|
||||
|
||||
SmallIcon = string.Empty;
|
||||
ShouldAutoCancel = false;
|
||||
LargeIcon = string.Empty;
|
||||
Style = NotificationStyle.None;
|
||||
Number = -1;
|
||||
UsesStopwatch = false;
|
||||
IntentData = string.Empty;
|
||||
Group = string.Empty;
|
||||
GroupSummary = false;
|
||||
SortKey = string.Empty;
|
||||
GroupAlertBehaviour = GroupAlertBehaviours.GroupAlertAll;
|
||||
ShowTimestamp = false;
|
||||
ShowCustomTimestamp = false;
|
||||
m_BigPictureStyle = null;
|
||||
|
||||
m_RepeatInterval = (-1L).ToTimeSpan();
|
||||
m_Color = new Color(0, 0, 0, 0);
|
||||
m_CustomTimestamp = (-1L).ToDatetime();
|
||||
m_SilentInForeground = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a repeatable notification struct with all optional fields set to default values.
|
||||
/// </summary>
|
||||
/// <param name="title">Notification title</param>
|
||||
/// <param name="text">Text to show on notification</param>
|
||||
/// <param name="fireTime">Date and time when to show, can be DateTime.Now to show right away</param>
|
||||
/// <param name="repeatInterval">Makes notification repeatable with this time interval</param>
|
||||
/// <remarks>
|
||||
/// There is a minimum period of 1 minute for repeating notifications.
|
||||
/// </remarks>
|
||||
public AndroidNotification(string title, string text, DateTime fireTime, TimeSpan repeatInterval)
|
||||
: this(title, text, fireTime)
|
||||
{
|
||||
RepeatInterval = repeatInterval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a notification struct with a custom small icon and all optional fields set to default values.
|
||||
/// </summary>
|
||||
/// <param name="title">Notification title</param>
|
||||
/// <param name="text">Text to show on notification</param>
|
||||
/// <param name="fireTime">Date and time when to show, can be DateTime.Now to show right away</param>
|
||||
/// <param name="repeatInterval">Makes notification repeatable with this time interval</param>
|
||||
/// <param name="smallIcon">Name of the small icon to be shown on notification</param>
|
||||
public AndroidNotification(string title, string text, DateTime fireTime, TimeSpan repeatInterval, string smallIcon)
|
||||
: this(title, text, fireTime, repeatInterval)
|
||||
{
|
||||
SmallIcon = smallIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5920d00d82f60ad438e2d483048328d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
class NotificationCallback : AndroidJavaProxy
|
||||
{
|
||||
public NotificationCallback() : base("com.unity.androidnotifications.NotificationCallback")
|
||||
{
|
||||
}
|
||||
|
||||
public override AndroidJavaObject Invoke(string methodName, AndroidJavaObject[] args)
|
||||
{
|
||||
if (methodName.Equals("onSentNotification", StringComparison.InvariantCulture) && args != null && args.Length == 1)
|
||||
{
|
||||
onSentNotification(args[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
return base.Invoke(methodName, args);
|
||||
}
|
||||
|
||||
public void onSentNotification(AndroidJavaObject notification)
|
||||
{
|
||||
AndroidReceivedNotificationMainThreadDispatcher.GetInstance().EnqueueReceivedNotification(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df44dfe6f4b394ca988636822db28499
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1300
File diff suppressed because it is too large
Load Diff
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 330d6c453457d452dba1912232a97e01
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// The level of interruption of this notification channel.
|
||||
/// The importance of a notification is used to determine how much the notification should interrupt the user (visually and audibly).
|
||||
/// The higher the importance of a notification, the more interruptive the notification will be.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The exact behaviour of each importance level might vary depending on the device and OS version on devices running Android 7.1 or older.
|
||||
/// </remarks>
|
||||
public enum Importance
|
||||
{
|
||||
/// <summary>
|
||||
/// A notification with no importance: does not show in the shade.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Low importance, notification is shown everywhere, but is not intrusive.
|
||||
/// </summary>
|
||||
Low = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Default importance, notification is shown everywhere, makes noise, but does not intrude visually.
|
||||
/// </summary>
|
||||
Default = 3,
|
||||
|
||||
/// <summary>
|
||||
/// High importance, notification is shown everywhere, makes noise and is shown on the screen.
|
||||
/// </summary>
|
||||
High = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether notifications appear on the lock screen.
|
||||
/// </summary>
|
||||
public enum LockScreenVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not reveal any part of this notification on a secure lock screen.
|
||||
/// </summary>
|
||||
Secret = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Show this notification on all lock screens, but conceal sensitive or private information on secure lock screens.
|
||||
/// </summary>
|
||||
Private = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Show this notification in its entirety on the lock screen.
|
||||
/// </summary>
|
||||
Public = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wrapper of the Android notification channel. Use this to group notifications by groups.
|
||||
/// </summary>
|
||||
public struct AndroidNotificationChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// Notification channel identifier.
|
||||
/// Must be specified when scheduling notifications.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notification channel name which is visible to users.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User visible description of the notification channel.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the registered channel group this channel belongs to.
|
||||
/// </summary>
|
||||
public string Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Importance level which is applied to all notifications sent to the channel.
|
||||
/// This can be changed by users in the settings app. Android uses importance to determine how much the notification should interrupt the user (visually and audibly).
|
||||
/// The higher the importance of a notification, the more interruptive the notification will be.
|
||||
/// The possible importance levels are the following:
|
||||
/// High: Makes a sound and appears as a heads-up notification.
|
||||
/// Default: Makes a sound.
|
||||
/// Low: No sound.
|
||||
/// None: No sound and does not appear in the status bar.
|
||||
/// </summary>
|
||||
public Importance Importance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not notifications posted to this channel can bypass the Do Not Disturb.
|
||||
/// This can be changed by users in the settings app.
|
||||
/// </summary>
|
||||
public bool CanBypassDnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether notifications posted to this channel can appear as badges in a Launcher application.
|
||||
/// </summary>
|
||||
public bool CanShowBadge { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether notifications posted to this channel should display notification lights, on devices that support that feature.
|
||||
/// This can be changed by users in the settings app.
|
||||
/// </summary>/
|
||||
public bool EnableLights { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether notification posted to this channel should vibrate.
|
||||
/// This can be changed by users in the settings app.
|
||||
/// </summary>
|
||||
public bool EnableVibration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the vibration pattern for notifications posted to this channel.
|
||||
/// </summary>
|
||||
public long[] VibrationPattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether or not notifications posted to this channel are shown on the lockscreen in full or redacted form.
|
||||
/// This can be changed by users in the settings app.
|
||||
/// </summary>
|
||||
public LockScreenVisibility LockScreenVisibility { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a channel is enabled in the Settings app. User can block notifications for the entire app or for individual channels. The Importance of a blocked channel is set to None.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return Importance != Importance.None; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a notification channel struct with all optional fields set to default values.
|
||||
/// </summary>
|
||||
/// <param name="id">ID for the channel</param>
|
||||
/// <param name="name">Channel name</param>
|
||||
/// <param name="description">Channel description</param>
|
||||
/// <param name="importance">Importance of the channel</param>
|
||||
public AndroidNotificationChannel(string id, string name, string description, Importance importance)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Group = null;
|
||||
this.Importance = importance;
|
||||
|
||||
CanBypassDnd = false;
|
||||
CanShowBadge = true;
|
||||
EnableLights = false;
|
||||
EnableVibration = true;
|
||||
VibrationPattern = null;
|
||||
|
||||
this.LockScreenVisibility = LockScreenVisibility.Public;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notification channel group description.
|
||||
/// It is optional to put channels into groups, but looks nicer in Settings UI.
|
||||
/// </summary>
|
||||
public struct AndroidNotificationChannelGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique ID for this group. Will rename the group if already exists.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A user visible name for this group.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A description for this group.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f3829a6c0515ee45b4e641fbd0999f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
internal static class AndroidNotificationExtensions
|
||||
{
|
||||
public static Importance ToImportance(this int importance)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(Importance), importance))
|
||||
return (Importance)importance;
|
||||
|
||||
return Importance.Default;
|
||||
}
|
||||
|
||||
public static LockScreenVisibility ToLockScreenVisibility(this int lockscreenVisibility)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(LockScreenVisibility), lockscreenVisibility))
|
||||
return (LockScreenVisibility)lockscreenVisibility;
|
||||
|
||||
return LockScreenVisibility.Public;
|
||||
}
|
||||
|
||||
public static NotificationStyle ToNotificationStyle(this int notificationStyle)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(NotificationStyle), notificationStyle))
|
||||
return (NotificationStyle)notificationStyle;
|
||||
|
||||
return NotificationStyle.None;
|
||||
}
|
||||
|
||||
public static GroupAlertBehaviours ToGroupAlertBehaviours(this int groupAlertBehaviour)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(GroupAlertBehaviours), groupAlertBehaviour))
|
||||
return (GroupAlertBehaviours)groupAlertBehaviour;
|
||||
|
||||
return GroupAlertBehaviours.GroupAlertAll;
|
||||
}
|
||||
|
||||
public static Color ToColor(this int color)
|
||||
{
|
||||
int a = (color >> 24) & 0xff;
|
||||
int r = (color >> 16) & 0xff;
|
||||
int g = (color >> 8) & 0xff;
|
||||
int b = (color) & 0xff;
|
||||
|
||||
return new Color32((byte)r, (byte)g, (byte)b, (byte)a);
|
||||
}
|
||||
|
||||
public static int ToInt(this Color? color)
|
||||
{
|
||||
if (!color.HasValue)
|
||||
return 0;
|
||||
|
||||
var color32 = (Color32)color.Value;
|
||||
return (color32.a & 0xff) << 24 | (color32.r & 0xff) << 16 | (color32.g & 0xff) << 8 | (color32.b & 0xff);
|
||||
}
|
||||
|
||||
public static long ToLong(this DateTime dateTime)
|
||||
{
|
||||
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
TimeSpan diff = dateTime.ToUniversalTime() - origin;
|
||||
|
||||
return (long)Math.Floor(diff.TotalMilliseconds);
|
||||
}
|
||||
|
||||
public static DateTime ToDatetime(this long dateTime)
|
||||
{
|
||||
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
return origin.AddMilliseconds(dateTime).ToLocalTime();
|
||||
}
|
||||
|
||||
public static long ToLong(this TimeSpan? timeSpan)
|
||||
{
|
||||
return timeSpan.HasValue ? (long)timeSpan.Value.TotalMilliseconds : -1L;
|
||||
}
|
||||
|
||||
public static TimeSpan ToTimeSpan(this long timeSpan)
|
||||
{
|
||||
return TimeSpan.FromMilliseconds(timeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b97d256371bb62248bb9f4404a72030a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for the AndroidNotification. Contains the notification's id and channel.
|
||||
/// </summary>
|
||||
public class AndroidNotificationIntentData
|
||||
{
|
||||
/// <summary>
|
||||
/// The id of the notification.
|
||||
/// </summary>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The channel id that the notification was sent to.
|
||||
/// </summary>
|
||||
public string Channel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the AndroidNotification.
|
||||
/// </summary>
|
||||
public AndroidNotification Notification { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the proxy to the Android Java instance of Notification class.
|
||||
/// </summary>
|
||||
public AndroidJavaObject NativeNotification { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an AndroidNotificationIntentData with AndroidNotification, id, and channel id.
|
||||
/// </summary>
|
||||
/// <param name="id">Notification id</param>
|
||||
/// <param name="channelId">ID of the notification channel</param>
|
||||
/// <param name="notification">Data of the received notification</param>
|
||||
public AndroidNotificationIntentData(int id, string channelId, AndroidNotification notification)
|
||||
{
|
||||
Id = id;
|
||||
Channel = channelId;
|
||||
Notification = notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25b01cf50533ff64387fe382f4075a2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that queues the received notifications and triggers the notification callbacks.
|
||||
/// </summary>
|
||||
public class AndroidReceivedNotificationMainThreadDispatcher : MonoBehaviour
|
||||
{
|
||||
private static AndroidReceivedNotificationMainThreadDispatcher instance = null;
|
||||
|
||||
private List<AndroidJavaObject> m_ReceivedNotificationQueue = new List<AndroidJavaObject>();
|
||||
|
||||
private List<AndroidJavaObject> m_ReceivedNotificationList = new List<AndroidJavaObject>();
|
||||
|
||||
internal void EnqueueReceivedNotification(AndroidJavaObject notification)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_ReceivedNotificationQueue.Add(notification);
|
||||
}
|
||||
}
|
||||
|
||||
internal static AndroidReceivedNotificationMainThreadDispatcher GetInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update is called once per frame.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
// Note: Don't call callbacks while locking receivedNotificationQueue, otherwise there's a risk
|
||||
// that callback might introduce an operations which would create a deadlock
|
||||
lock (this)
|
||||
{
|
||||
if (m_ReceivedNotificationQueue.Count == 0)
|
||||
return;
|
||||
var temp = m_ReceivedNotificationQueue;
|
||||
m_ReceivedNotificationQueue = m_ReceivedNotificationList;
|
||||
m_ReceivedNotificationList = temp;
|
||||
}
|
||||
|
||||
foreach (var notification in m_ReceivedNotificationList)
|
||||
{
|
||||
try
|
||||
{
|
||||
AndroidNotificationCenter.ReceivedNotificationCallback(notification);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
m_ReceivedNotificationList.Clear();
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
DontDestroyOnLoad(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 253fc88305aec43f3886f5484cfbb307
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Assets/MYp0ZVTT2QSDK/ThirdParty/com.unity.mobile.notifications@2.3.2/Runtime/Android/AssemblyInfo.cs
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.Android.Notifications.Tests")]
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76998f4bffc9142d18bb6fa7e954e01c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
using UnityEngine.Android;
|
||||
|
||||
namespace Unity.Notifications.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a status of the Android runtime permission.
|
||||
/// </summary>
|
||||
public enum PermissionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No permission as user was not prompted for it.
|
||||
/// </summary>
|
||||
NotRequested = 0,
|
||||
|
||||
/// <summary>
|
||||
/// User gave permission.
|
||||
/// </summary>
|
||||
Allowed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// User denied permission.
|
||||
/// </summary>
|
||||
Denied = 2,
|
||||
|
||||
/// <summary>
|
||||
/// No longer used. User denied permission and expressed intent to not be prompted again.
|
||||
/// </summary>
|
||||
DeniedDontAskAgain = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A request for permission was made and user hasn't responded yet.
|
||||
/// </summary>
|
||||
RequestPending = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Notifications are blocked for this app. Before API level 33 this means they were disabled in Settings.
|
||||
/// <see cref="https://developer.android.com/reference/android/app/NotificationManager#areNotificationsEnabled()"/>
|
||||
/// </summary>
|
||||
NotificationsBlockedForApp = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class to request permission to post notifications.
|
||||
/// Before Android 13 (API 33) it is not required and Status will become Allowed immediately.
|
||||
/// May succeed or fail immediately. Users response is saved to PlayerPrefs.
|
||||
/// Respects users wish to not be asked again.
|
||||
/// </summary>
|
||||
/// <seealso cref="AndroidNotificationCenter.UserPermissionToPost"/>
|
||||
/// <seealso cref="AndroidNotificationCenter.SETTING_POST_NOTIFICATIONS_PERMISSION"/>
|
||||
public class PermissionRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The status of this request.
|
||||
/// Value other than RequestPending means request has completed.
|
||||
/// </summary>
|
||||
public PermissionStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new request.
|
||||
/// Will show user a dialog asking for permission if that is required to post notifications and user hasn't permanently denied it already.
|
||||
/// </summary>
|
||||
public PermissionRequest()
|
||||
{
|
||||
Status = AndroidNotificationCenter.UserPermissionToPost;
|
||||
switch (Status)
|
||||
{
|
||||
case PermissionStatus.NotRequested:
|
||||
case PermissionStatus.Denied:
|
||||
case PermissionStatus.DeniedDontAskAgain: // this one is no longer used, but might be found in settings
|
||||
Status = RequestPermission();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PermissionStatus RequestPermission()
|
||||
{
|
||||
if (!AndroidNotificationCenter.CanRequestPermissionToPost)
|
||||
return PermissionStatus.Denied;
|
||||
var callbacks = new PermissionCallbacks();
|
||||
callbacks.PermissionGranted += (unused) => PermissionResponse(PermissionStatus.Allowed);
|
||||
callbacks.PermissionDenied += (unused) => PermissionResponse(PermissionStatus.Denied);
|
||||
Permission.RequestUserPermission(AndroidNotificationCenter.PERMISSION_POST_NOTIFICATIONS, callbacks);
|
||||
return PermissionStatus.RequestPending;
|
||||
}
|
||||
|
||||
void PermissionResponse(PermissionStatus status)
|
||||
{
|
||||
Status = status;
|
||||
AndroidNotificationCenter.SetPostPermissionSetting(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 110fbd120961b4105a52b39fc24273be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4d5e3b0fd3074704a1c4fcee255f85c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a06b648d726ee0479a00bac712c49f1
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 0
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
def getCompileSdk(unityLib) {
|
||||
def unityCompileSdk = unityLib.compileSdkVersion
|
||||
def version = unityCompileSdk.find('\\d+').toInteger()
|
||||
if (version < 33) {
|
||||
return 33
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
android {
|
||||
// Checking if namespace exists is needed for 2021.3 (AGP 4.0.1)
|
||||
// When 2021.3 is dropped, remove this check and package="com.unity.androidnotifications" from AndroidManifest
|
||||
if (project.android.hasProperty("namespace")) {
|
||||
namespace "com.unity.androidnotifications"
|
||||
}
|
||||
|
||||
def unityLib = project(':unityLibrary').extensions.getByName('android')
|
||||
compileSdkVersion getCompileSdk(unityLib)
|
||||
buildToolsVersion unityLib.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 22
|
||||
consumerProguardFiles "proguard-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api files('../libs/unity-classes.jar')
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Accessed from C# code
|
||||
-keep class com.unity.androidnotifications.UnityNotificationManager { public *; }
|
||||
-keep interface com.unity.androidnotifications.NotificationCallback { *; }
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity.androidnotifications">
|
||||
<application>
|
||||
<receiver android:name="com.unity.androidnotifications.UnityNotificationManager" android:exported="false" />
|
||||
<receiver android:name="com.unity.androidnotifications.UnityNotificationRestartReceiver" android:enabled="false" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<meta-data android:name="com.unity.androidnotifications.exact_scheduling" android:value="0" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
</manifest>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class UnityNotificationBackgroundThread extends Thread {
|
||||
private static abstract class Task {
|
||||
// returns true if notificationIds was modified (needs to be saved)
|
||||
public abstract boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications);
|
||||
}
|
||||
|
||||
private static class ScheduleNotificationTask extends Task {
|
||||
private int notificationId;
|
||||
private Notification.Builder notificationBuilder;
|
||||
private boolean isCustomized;
|
||||
private boolean isNew;
|
||||
|
||||
public ScheduleNotificationTask(int id, Notification.Builder builder, boolean customized, boolean addedNew) {
|
||||
notificationId = id;
|
||||
notificationBuilder = builder;
|
||||
isCustomized = customized;
|
||||
isNew = addedNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
String id = String.valueOf(notificationId);
|
||||
Integer ID = Integer.valueOf(notificationId);
|
||||
boolean didSchedule = false;
|
||||
try {
|
||||
UnityNotificationManager.mUnityNotificationManager.performNotificationScheduling(notificationId, notificationBuilder, isCustomized);
|
||||
didSchedule = true;
|
||||
} finally {
|
||||
// if failed to schedule or replace, remove
|
||||
if (!didSchedule) {
|
||||
notifications.remove(notificationId);
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
manager.deleteExpiredNotificationIntent(id);
|
||||
}
|
||||
}
|
||||
|
||||
return isNew;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CancelNotificationTask extends Task {
|
||||
private int notificationId;
|
||||
|
||||
public CancelNotificationTask(int id) {
|
||||
notificationId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
if (notifications.remove(notificationId) != null) {
|
||||
manager.deleteExpiredNotificationIntent(String.valueOf(notificationId));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CancelAllNotificationsTask extends Task {
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
if (notifications.isEmpty())
|
||||
return false;
|
||||
|
||||
Enumeration<Integer> ids = notifications.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
Integer notificationId = ids.nextElement();
|
||||
manager.cancelPendingNotificationIntent(notificationId);
|
||||
manager.deleteExpiredNotificationIntent(String.valueOf(notificationId));
|
||||
}
|
||||
|
||||
notifications.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HousekeepingTask extends Task {
|
||||
UnityNotificationBackgroundThread thread;
|
||||
|
||||
public HousekeepingTask(UnityNotificationBackgroundThread th) {
|
||||
thread = th;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
HashSet<String> notificationIds = new HashSet<>();
|
||||
Enumeration<Integer> ids = notifications.keys();
|
||||
while (ids.hasMoreElements()) {
|
||||
notificationIds.add(String.valueOf(ids.nextElement()));
|
||||
}
|
||||
thread.performHousekeeping(notificationIds);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int TASKS_FOR_HOUSEKEEPING = 50;
|
||||
private LinkedTransferQueue<Task> mTasks = new LinkedTransferQueue();
|
||||
private ConcurrentHashMap<Integer, Notification.Builder> mScheduledNotifications;
|
||||
private UnityNotificationManager mManager;
|
||||
private int mTasksSinceHousekeeping = TASKS_FOR_HOUSEKEEPING; // we want hoursekeeping at the start
|
||||
|
||||
public UnityNotificationBackgroundThread(UnityNotificationManager manager, ConcurrentHashMap<Integer, Notification.Builder> scheduledNotifications) {
|
||||
mManager = manager;
|
||||
mScheduledNotifications = scheduledNotifications;
|
||||
// rescheduling after reboot may have loaded, otherwise load here
|
||||
if (mScheduledNotifications.size() == 0)
|
||||
loadNotifications();
|
||||
}
|
||||
|
||||
public void enqueueNotification(int id, Notification.Builder notificationBuilder, boolean customized, boolean addedNew) {
|
||||
mTasks.add(new UnityNotificationBackgroundThread.ScheduleNotificationTask(id, notificationBuilder, customized, addedNew));
|
||||
}
|
||||
|
||||
public void enqueueCancelNotification(int id) {
|
||||
mTasks.add(new CancelNotificationTask(id));
|
||||
}
|
||||
|
||||
public void enqueueCancelAllNotifications() {
|
||||
mTasks.add(new CancelAllNotificationsTask());
|
||||
}
|
||||
|
||||
private void enqueueHousekeeping() {
|
||||
mTasks.add(new HousekeepingTask(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean haveChanges = false;
|
||||
while (true) {
|
||||
try {
|
||||
Task task = mTasks.take();
|
||||
haveChanges |= executeTask(mManager, task, mScheduledNotifications);
|
||||
if (!(task instanceof HousekeepingTask))
|
||||
++mTasksSinceHousekeeping;
|
||||
if (mTasks.size() == 0 && haveChanges) {
|
||||
haveChanges = false;
|
||||
enqueueHousekeeping();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
if (mTasks.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeTask(UnityNotificationManager manager, Task task, ConcurrentHashMap<Integer, Notification.Builder> notifications) {
|
||||
try {
|
||||
return task.run(manager, notifications);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Exception executing notification task", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void performHousekeeping(Set<String> notificationIds) {
|
||||
// don't do housekeeping if last task we did was housekeeping (other=1)
|
||||
boolean performHousekeeping = mTasksSinceHousekeeping >= TASKS_FOR_HOUSEKEEPING;
|
||||
mTasksSinceHousekeeping = 0;
|
||||
if (performHousekeeping)
|
||||
mManager.performNotificationHousekeeping(notificationIds);
|
||||
mManager.saveScheduledNotificationIDs(notificationIds);
|
||||
}
|
||||
|
||||
private void loadNotifications() {
|
||||
List<Notification.Builder> notifications = mManager.loadSavedNotifications();
|
||||
if (notifications == null || notifications.size() == 0)
|
||||
return;
|
||||
final long currentTime = Calendar.getInstance().getTime().getTime();
|
||||
boolean needHousekeeping = false;
|
||||
for (Notification.Builder builder : notifications) {
|
||||
Bundle extras = builder.getExtras();
|
||||
int id = extras.getInt(KEY_ID, -1);
|
||||
long fireTime = extras.getLong(KEY_FIRE_TIME, -1);
|
||||
boolean inFuture = fireTime - currentTime > 0;
|
||||
if (inFuture)
|
||||
mScheduledNotifications.put(id, builder);
|
||||
else
|
||||
needHousekeeping = true;
|
||||
}
|
||||
|
||||
if (needHousekeeping)
|
||||
enqueueHousekeeping();
|
||||
}
|
||||
}
|
||||
+1117
File diff suppressed because it is too large
Load Diff
+67
@@ -0,0 +1,67 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_REPEAT_INTERVAL;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
public class UnityNotificationRestartReceiver extends BroadcastReceiver {
|
||||
private static final long EXPIRATION_TRESHOLD = 600000; // 10 minutes
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent received_intent) {
|
||||
Log.d(TAG_UNITY, "Rescheduling notifications after restart");
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(received_intent.getAction())) {
|
||||
AsyncTask.execute(() -> { rescheduleSavedNotifications(context); });
|
||||
}
|
||||
}
|
||||
|
||||
private static void rescheduleSavedNotifications(Context context) {
|
||||
UnityNotificationManager manager = UnityNotificationManager.getNotificationManagerImpl(context);
|
||||
List<Notification.Builder> saved_notifications = manager.loadSavedNotifications();
|
||||
Date currentDate = Calendar.getInstance().getTime();
|
||||
|
||||
for (Notification.Builder notificationBuilder : saved_notifications) {
|
||||
rescheduleNotification(manager, currentDate, notificationBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean rescheduleNotification(UnityNotificationManager manager, Date currentDate, Notification.Builder notificationBuilder) {
|
||||
try {
|
||||
Bundle extras = notificationBuilder.getExtras();
|
||||
long repeatInterval = extras.getLong(KEY_REPEAT_INTERVAL, 0L);
|
||||
long fireTime = extras.getLong(KEY_FIRE_TIME, 0L);
|
||||
Date fireTimeDate = new Date(fireTime);
|
||||
|
||||
boolean isRepeatable = repeatInterval > 0;
|
||||
|
||||
if (fireTimeDate.after(currentDate) || isRepeatable) {
|
||||
manager.scheduleAlarmWithNotification(notificationBuilder);
|
||||
return true;
|
||||
} else if (currentDate.getTime() - fireTime < EXPIRATION_TRESHOLD) {
|
||||
// notification is in the past, but not by much, send now
|
||||
int id = extras.getInt(KEY_ID);
|
||||
manager.notify(id, notificationBuilder);
|
||||
return true;
|
||||
} else {
|
||||
Log.d(TAG_UNITY, "Notification expired, not rescheduling, ID: " + extras.getInt(KEY_ID, -1));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to reschedule notification", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+652
@@ -0,0 +1,652 @@
|
||||
package com.unity.androidnotifications;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_CHANNEL_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_FIRE_TIME;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_ID;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_INTENT_DATA;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_LARGE_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_NOTIFICATION;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_REPEAT_INTERVAL;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_SMALL_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_SHOW_IN_FOREGROUND;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_LARGE_ICON;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_PICTURE;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_CONTENT_TITLE;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_SUMMARY_TEXT;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_CONTENT_DESCRIPTION;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.KEY_BIG_SHOW_WHEN_COLLAPSED;
|
||||
import static com.unity.androidnotifications.UnityNotificationManager.TAG_UNITY;
|
||||
|
||||
class UnityNotificationUtilities {
|
||||
/*
|
||||
We serialize notifications and save them to shared prefs, so that if app is killed, we can recreate them.
|
||||
The serialized BLOB starts with a four byte magic number descibing serialization type, followed by an integer version.
|
||||
IMPORTANT: IF YOU DO A CHANGE THAT AFFECTS THE LAYOUT, BUMP THE VERSION, AND ENSURE OLD VERSION STILL DESERIALIZES. ADD TEST.
|
||||
In real life app can get updated having old serialized notifications present, so we should be able to deserialize them.
|
||||
*/
|
||||
// magic stands for "Unity Mobile Notifications Notification"
|
||||
static final byte[] UNITY_MAGIC_NUMBER = new byte[] { 'U', 'M', 'N', 'N'};
|
||||
private static final byte[] UNITY_MAGIC_NUMBER_PARCELLED = new byte[] { 'U', 'M', 'N', 'P'};
|
||||
private static final int NOTIFICATION_SERIALIZATION_VERSION = 3;
|
||||
private static final int INTENT_SERIALIZATION_VERSION = 0;
|
||||
|
||||
static final String SAVED_NOTIFICATION_PRIMARY_KEY = "data";
|
||||
static final String SAVED_NOTIFICATION_FALLBACK_KEY = "fallback.data";
|
||||
|
||||
protected static int findResourceIdInContextByName(Context context, String name) {
|
||||
if (name == null)
|
||||
return 0;
|
||||
|
||||
try {
|
||||
Resources res = context.getResources();
|
||||
if (res != null) {
|
||||
int id = res.getIdentifier(name, "mipmap", context.getPackageName());
|
||||
if (id == 0)
|
||||
return res.getIdentifier(name, "drawable", context.getPackageName());
|
||||
else
|
||||
return id;
|
||||
}
|
||||
return 0;
|
||||
} catch (Resources.NotFoundException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Originally we used to serialize a bundle with predefined list of values.
|
||||
After we exposed entire Notification.Builder to users, this is not sufficient anymore.
|
||||
Unfortunately, while Notification itself is Parcelable and can be marshalled to bytes,
|
||||
it's contents are not guaranteed to be (Binder objects).
|
||||
Hence what we try to do here is:
|
||||
- serialize as is if notification is possibly customized by user
|
||||
- otherwise serialize our stuff, since there is nothing more
|
||||
*/
|
||||
protected static void serializeNotification(SharedPreferences prefs, Notification notification, boolean serializeParcel) {
|
||||
try {
|
||||
String serialized;
|
||||
ByteArrayOutputStream data = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(data);
|
||||
if (serializeParcel) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(KEY_NOTIFICATION, notification);
|
||||
if (serializeNotificationParcel(intent, out)) {
|
||||
out.close();
|
||||
byte[] bytes = data.toByteArray();
|
||||
serialized = Base64.encodeToString(bytes, 0, bytes.length, 0);
|
||||
} else {
|
||||
return; // failed
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (serializeNotificationCustom(notification, out)) {
|
||||
out.flush();
|
||||
byte[] bytes = data.toByteArray();
|
||||
serialized = Base64.encodeToString(bytes, 0, bytes.length, 0);
|
||||
} else {
|
||||
return; // failed
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit().clear();
|
||||
editor.putString(SAVED_NOTIFICATION_PRIMARY_KEY, serialized);
|
||||
editor.apply();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean serializeNotificationParcel(Intent intent, DataOutputStream out) {
|
||||
try {
|
||||
byte[] bytes = serializeParcelable(intent);
|
||||
if (bytes == null || bytes.length == 0)
|
||||
return false;
|
||||
out.write(UNITY_MAGIC_NUMBER_PARCELLED);
|
||||
out.writeInt(INTENT_SERIALIZATION_VERSION);
|
||||
out.writeInt(bytes.length);
|
||||
out.write(bytes);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification as Parcel", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification as Parcel", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean serializeNotificationCustom(Notification notification, DataOutputStream out) {
|
||||
try {
|
||||
out.write(UNITY_MAGIC_NUMBER);
|
||||
out.writeInt(NOTIFICATION_SERIALIZATION_VERSION);
|
||||
|
||||
// serialize extras
|
||||
boolean showWhen = notification.extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false);
|
||||
|
||||
out.writeInt(notification.extras.getInt(KEY_ID));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
serializeString(out, notification.extras.getString(KEY_SMALL_ICON));
|
||||
serializeString(out, notification.extras.getString(KEY_LARGE_ICON));
|
||||
out.writeLong(notification.extras.getLong(KEY_FIRE_TIME, -1));
|
||||
out.writeLong(notification.extras.getLong(KEY_REPEAT_INTERVAL, -1));
|
||||
serializeString(out, notification.extras.getString(Notification.EXTRA_BIG_TEXT));
|
||||
out.writeBoolean(notification.extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false));
|
||||
out.writeBoolean(showWhen);
|
||||
serializeString(out, notification.extras.getString(KEY_INTENT_DATA));
|
||||
out.writeBoolean(notification.extras.getBoolean(KEY_SHOW_IN_FOREGROUND, true));
|
||||
|
||||
String bigPicture = notification.extras.getString(KEY_BIG_PICTURE);
|
||||
serializeString(out, bigPicture);
|
||||
if (bigPicture != null && bigPicture.length() > 0) {
|
||||
// the following only need to be put in if big picture is there
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_LARGE_ICON));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_CONTENT_TITLE));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_CONTENT_DESCRIPTION));
|
||||
serializeString(out, notification.extras.getString(KEY_BIG_SUMMARY_TEXT));
|
||||
out.writeBoolean(notification.extras.getBoolean(KEY_BIG_SHOW_WHEN_COLLAPSED, false));
|
||||
}
|
||||
|
||||
serializeString(out, Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? null : notification.getChannelId());
|
||||
Integer color = UnityNotificationManager.getNotificationColor(notification);
|
||||
out.writeBoolean(color != null);
|
||||
if (color != null)
|
||||
out.writeInt(color);
|
||||
out.writeInt(notification.number);
|
||||
out.writeBoolean(0 != (notification.flags & Notification.FLAG_AUTO_CANCEL));
|
||||
serializeString(out, notification.getGroup());
|
||||
out.writeBoolean(0 != (notification.flags & Notification.FLAG_GROUP_SUMMARY));
|
||||
out.writeInt(UnityNotificationManager.getNotificationGroupAlertBehavior(notification));
|
||||
serializeString(out, notification.getSortKey());
|
||||
if (showWhen)
|
||||
out.writeLong(notification.when);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize notification", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void serializeString(DataOutputStream out, String s) throws IOException {
|
||||
if (s == null || s.length() == 0)
|
||||
out.writeInt(0);
|
||||
else {
|
||||
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
out.writeInt(bytes.length);
|
||||
out.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] serializeParcelable(Parcelable obj) {
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
Bundle b = new Bundle();
|
||||
b.putParcelable("obj", obj);
|
||||
p.writeParcelable(b, 0);
|
||||
byte[] result = p.marshall();
|
||||
p.recycle();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize Parcelable", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to serialize Parcelable", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static Object deserializeNotification(Context context, SharedPreferences prefs) {
|
||||
String serializedIntentData = prefs.getString(SAVED_NOTIFICATION_PRIMARY_KEY, "");
|
||||
if (null == serializedIntentData || serializedIntentData.length() <= 0)
|
||||
return null;
|
||||
byte[] bytes = Base64.decode(serializedIntentData, 0);
|
||||
Object notification = deserializeNotification(context, bytes);
|
||||
if (notification != null)
|
||||
return notification;
|
||||
serializedIntentData = prefs.getString(SAVED_NOTIFICATION_FALLBACK_KEY, "");
|
||||
if (null == serializedIntentData || serializedIntentData.length() <= 0)
|
||||
return null;
|
||||
bytes = Base64.decode(serializedIntentData, 0);
|
||||
return deserializeNotification(context, bytes);
|
||||
}
|
||||
|
||||
/* See serialization method above for explaination of fallbacks.
|
||||
This one matches it with one additional fallback: support for "old" bundle serialization.
|
||||
*/
|
||||
private static Object deserializeNotification(Context context, byte[] bytes) {
|
||||
ByteArrayInputStream data = new ByteArrayInputStream(bytes);
|
||||
DataInputStream in = new DataInputStream(data);
|
||||
Notification notification = deserializeNotificationParcelable(in);
|
||||
if (notification != null)
|
||||
return notification;
|
||||
data.reset();
|
||||
Notification.Builder builder = deserializeNotificationCustom(context, in);
|
||||
if (builder == null) {
|
||||
builder = deserializedFromOldIntent(context, bytes);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static boolean readAndCheckMagicNumber(DataInputStream in, byte[] magic) {
|
||||
try {
|
||||
boolean magicNumberMatch = true;
|
||||
for (int i = 0; i < magic.length; ++i) {
|
||||
byte b = in.readByte();
|
||||
if (b != magic[i]) {
|
||||
magicNumberMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return magicNumberMatch;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Notification deserializeNotificationParcelable(DataInputStream in) {
|
||||
try {
|
||||
if (!readAndCheckMagicNumber(in, UNITY_MAGIC_NUMBER_PARCELLED))
|
||||
return null;
|
||||
int version = in.readInt();
|
||||
if (version < 0 || version > INTENT_SERIALIZATION_VERSION)
|
||||
return null;
|
||||
Intent intent = deserializeParcelable(in);
|
||||
Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
|
||||
return notification;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification intent", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification intent", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Notification.Builder deserializeNotificationCustom(Context context, DataInputStream in) {
|
||||
try {
|
||||
if (!readAndCheckMagicNumber(in, UNITY_MAGIC_NUMBER))
|
||||
return null;
|
||||
int version = in.readInt();
|
||||
if (version < 0 || version > NOTIFICATION_SERIALIZATION_VERSION)
|
||||
return null;
|
||||
|
||||
// deserialize extras
|
||||
int id = 0;
|
||||
String title, text, smallIcon, largeIcon, bigText, intentData;
|
||||
long fireTime, repeatInterval;
|
||||
boolean usesStopWatch, showWhen, showInForeground = true;
|
||||
Bundle extras = null;
|
||||
String bigPicture = null, bigLargeIcon = null, bigContentTitle = null, bigSummary = null, bigContentDesc = null;
|
||||
boolean bigShowWhenCollapsed = false;
|
||||
if (version < 2) {
|
||||
// no longer serialized since v2
|
||||
extras = deserializeParcelable(in);
|
||||
}
|
||||
|
||||
// before v2 it was extras or variables, since 2 always variables
|
||||
if (extras == null) {
|
||||
// extras serialized manually
|
||||
id = in.readInt();
|
||||
title = deserializeString(in);
|
||||
text = deserializeString(in);
|
||||
smallIcon = deserializeString(in);
|
||||
largeIcon = deserializeString(in);
|
||||
fireTime = in.readLong();
|
||||
repeatInterval = in.readLong();
|
||||
bigText = deserializeString(in);
|
||||
usesStopWatch = in.readBoolean();
|
||||
showWhen = in.readBoolean();
|
||||
intentData = deserializeString(in);
|
||||
if (version > 0)
|
||||
showInForeground = in.readBoolean();
|
||||
|
||||
if (version >= 3) {
|
||||
bigPicture = deserializeString(in);
|
||||
if (bigPicture != null && bigPicture.length() > 0) {
|
||||
// the following only need to be put in if big picture is there
|
||||
bigLargeIcon = deserializeString(in);
|
||||
bigContentTitle = deserializeString(in);
|
||||
bigContentDesc = deserializeString(in);
|
||||
bigSummary = deserializeString(in);
|
||||
bigShowWhenCollapsed = in.readBoolean();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
title = extras.getString(Notification.EXTRA_TITLE);
|
||||
text = extras.getString(Notification.EXTRA_TEXT);
|
||||
smallIcon = extras.getString(KEY_SMALL_ICON);
|
||||
largeIcon = extras.getString(KEY_LARGE_ICON);
|
||||
fireTime = extras.getLong(KEY_FIRE_TIME, -1);
|
||||
repeatInterval = extras.getLong(KEY_REPEAT_INTERVAL, -1);
|
||||
bigText = extras.getString(Notification.EXTRA_BIG_TEXT);
|
||||
usesStopWatch = extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false);
|
||||
showWhen = extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false);
|
||||
intentData = extras.getString(KEY_INTENT_DATA);
|
||||
}
|
||||
|
||||
String channelId = deserializeString(in);
|
||||
boolean haveColor = in.readBoolean();
|
||||
int color = 0;
|
||||
if (haveColor)
|
||||
color = in.readInt();
|
||||
int number = in.readInt();
|
||||
boolean shouldAutoCancel = in.readBoolean();
|
||||
String group = deserializeString(in);
|
||||
boolean groupSummary = in.readBoolean();
|
||||
int groupAlertBehavior = in.readInt();
|
||||
String sortKey = deserializeString(in);
|
||||
long when = showWhen ? in.readLong() : 0;
|
||||
|
||||
UnityNotificationManager manager = UnityNotificationManager.getNotificationManagerImpl(context);
|
||||
Notification.Builder builder = manager.createNotificationBuilder(channelId);
|
||||
if (extras != null)
|
||||
builder.setExtras(extras);
|
||||
else {
|
||||
builder.getExtras().putInt(KEY_ID, id);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, smallIcon);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
if (fireTime != -1)
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, fireTime);
|
||||
if (repeatInterval != -1)
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, repeatInterval);
|
||||
if (intentData != null)
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
builder.getExtras().putBoolean(KEY_SHOW_IN_FOREGROUND, showInForeground);
|
||||
}
|
||||
if (title != null)
|
||||
builder.setContentTitle(title);
|
||||
if (text != null)
|
||||
builder.setContentText(text);
|
||||
if (bigText != null)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(bigText));
|
||||
else if (bigPicture != null)
|
||||
manager.setupBigPictureStyle(builder, bigLargeIcon, bigPicture, bigContentTitle, bigContentDesc, bigSummary, bigShowWhenCollapsed);
|
||||
if (haveColor)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
if (number >= 0)
|
||||
builder.setNumber(number);
|
||||
builder.setAutoCancel(shouldAutoCancel);
|
||||
UnityNotificationManager.setNotificationUsesChronometer(builder, usesStopWatch);
|
||||
if (group != null && group.length() > 0)
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(groupSummary);
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, groupAlertBehavior);
|
||||
if (sortKey != null && sortKey.length() > 0)
|
||||
builder.setSortKey(sortKey);
|
||||
if (showWhen) {
|
||||
builder.setShowWhen(true);
|
||||
builder.setWhen(when);
|
||||
}
|
||||
|
||||
return builder;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize notification", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Notification.Builder deserializedFromOldIntent(Context context, byte[] bytes) {
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
p.unmarshall(bytes, 0, bytes.length);
|
||||
p.setDataPosition(0);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.readFromParcel(p);
|
||||
|
||||
int id = bundle.getInt(KEY_ID, -1);
|
||||
String channelId = bundle.getString("channelID");
|
||||
String textTitle = bundle.getString("textTitle");
|
||||
String textContent = bundle.getString("textContent");
|
||||
String smallIcon = bundle.getString("smallIconStr");
|
||||
boolean autoCancel = bundle.getBoolean("autoCancel", false);
|
||||
boolean usesChronometer = bundle.getBoolean("usesChronometer", false);
|
||||
long fireTime = bundle.getLong(KEY_FIRE_TIME, -1);
|
||||
long repeatInterval = bundle.getLong(KEY_REPEAT_INTERVAL, -1);
|
||||
String largeIcon = bundle.getString("largeIconStr");
|
||||
int style = bundle.getInt("style", -1);
|
||||
int color = bundle.getInt("color", 0);
|
||||
int number = bundle.getInt("number", 0);
|
||||
String intentData = bundle.getString(KEY_INTENT_DATA);
|
||||
String group = bundle.getString("group");
|
||||
boolean groupSummary = bundle.getBoolean("groupSummary", false);
|
||||
String sortKey = bundle.getString("sortKey");
|
||||
int groupAlertBehaviour = bundle.getInt("groupAlertBehaviour", -1);
|
||||
boolean showTimestamp = bundle.getBoolean("showTimestamp", false);
|
||||
|
||||
Notification.Builder builder = UnityNotificationManager.getNotificationManagerImpl(context).createNotificationBuilder(channelId);
|
||||
builder.getExtras().putInt(KEY_ID, id);
|
||||
builder.setContentTitle(textTitle);
|
||||
builder.setContentText(textContent);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, smallIcon);
|
||||
builder.setAutoCancel(autoCancel);
|
||||
builder.setUsesChronometer(usesChronometer);
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, fireTime);
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, repeatInterval);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
if (style == 2)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(textContent));
|
||||
if (color != 0)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
if (number >= 0)
|
||||
builder.setNumber(number);
|
||||
if (intentData != null)
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
if (null != group && group.length() > 0)
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(groupSummary);
|
||||
if (null != sortKey && sortKey.length() > 0)
|
||||
builder.setSortKey(sortKey);
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, groupAlertBehaviour);
|
||||
builder.setShowWhen(showTimestamp);
|
||||
return builder;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize old style notification", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize old style notification", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String deserializeString(DataInputStream in) throws IOException {
|
||||
int length = in.readInt();
|
||||
if (length <= 0)
|
||||
return null;
|
||||
byte[] bytes = new byte[length];
|
||||
int didRead = in.read(bytes);
|
||||
if (didRead != bytes.length)
|
||||
throw new IOException("Insufficient amount of bytes read");
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static <T extends Parcelable> T deserializeParcelable(DataInputStream in) throws IOException {
|
||||
int length = in.readInt();
|
||||
if (length <= 0)
|
||||
return null;
|
||||
byte[] bytes = new byte[length];
|
||||
int didRead = in.read(bytes);
|
||||
if (didRead != bytes.length)
|
||||
throw new IOException("Insufficient amount of bytes read");
|
||||
|
||||
try {
|
||||
Parcel p = Parcel.obtain();
|
||||
p.unmarshall(bytes, 0, bytes.length);
|
||||
p.setDataPosition(0);
|
||||
Bundle b = p.readParcelable(UnityNotificationUtilities.class.getClassLoader());
|
||||
p.recycle();
|
||||
if (b != null) {
|
||||
return b.getParcelable("obj");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize parcelable", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to deserialize parcelable", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Returns Activity class to be opened when notification is tapped
|
||||
// Search is done in this order:
|
||||
// * class specified in meta-data key custom_notification_android_activity
|
||||
// * the only enabled activity with name ending in either .UnityPlayerActivity or .UnityPlayerGameActivity
|
||||
// * the only enabled activity in the package
|
||||
protected static Class<?> getOpenAppActivity(Context context) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
||||
Bundle bundle = ai.metaData;
|
||||
|
||||
if (bundle.containsKey("custom_notification_android_activity")) {
|
||||
try {
|
||||
return Class.forName(bundle.getString("custom_notification_android_activity"));
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG_UNITY, "Specified activity class for notifications not found: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Log.w(TAG_UNITY, "No custom_notification_android_activity found, attempting to find app activity class");
|
||||
|
||||
ActivityInfo[] aInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES).activities;
|
||||
if (aInfo == null) {
|
||||
Log.e(TAG_UNITY, "Could not get package activities");
|
||||
return null;
|
||||
}
|
||||
|
||||
String activityClassName = null;
|
||||
boolean activityIsUnity = false, activityConflict = false;
|
||||
for (ActivityInfo info : aInfo) {
|
||||
// activity alias not supported
|
||||
if (!info.enabled || info.targetActivity != null)
|
||||
continue;
|
||||
|
||||
boolean candidateIsUnity = isUnityActivity(info.name);
|
||||
if (activityClassName == null) {
|
||||
activityClassName = info.name;
|
||||
activityIsUnity = candidateIsUnity;
|
||||
continue;
|
||||
}
|
||||
|
||||
// two Unity activities is a hard conflict
|
||||
// two non-Unity activities is a conflict unless we find a Unity activity later on
|
||||
if (activityIsUnity == candidateIsUnity) {
|
||||
activityConflict = true;
|
||||
if (activityIsUnity && candidateIsUnity)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidateIsUnity) {
|
||||
activityClassName = info.name;
|
||||
activityIsUnity = candidateIsUnity;
|
||||
activityConflict = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (activityConflict) {
|
||||
Log.e(TAG_UNITY, "Multiple choices for activity for notifications, set activity explicitly in Notification Settings");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activityClassName == null) {
|
||||
Log.e(TAG_UNITY, "Activity class for notifications not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
return Class.forName(activityClassName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG_UNITY, "Failed to find activity class: " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isUnityActivity(String name) {
|
||||
return name.endsWith(".UnityPlayerActivity") || name.endsWith(".UnityPlayerGameActivity");
|
||||
}
|
||||
|
||||
protected static Notification.Builder recoverBuilder(Context context, Notification notification) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Notification.Builder builder = Notification.Builder.recoverBuilder(context, notification);
|
||||
// extras not recovered, transfer manually
|
||||
builder.setExtras(notification.extras);
|
||||
return builder;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG_UNITY, "Failed to recover builder for notification!", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG_UNITY, "Failed to recover builder for notification!", e);
|
||||
}
|
||||
|
||||
return recoverBuilderCustom(context, notification);
|
||||
}
|
||||
|
||||
private static Notification.Builder recoverBuilderCustom(Context context, Notification notification) {
|
||||
String channelID = notification.extras.getString(KEY_CHANNEL_ID);
|
||||
Notification.Builder builder = UnityNotificationManager.getNotificationManagerImpl(context).createNotificationBuilder(channelID);
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_SMALL_ICON, notification.extras.getString(KEY_SMALL_ICON));
|
||||
String largeIcon = notification.extras.getString(KEY_LARGE_ICON);
|
||||
if (largeIcon != null && !largeIcon.isEmpty())
|
||||
UnityNotificationManager.setNotificationIcon(builder, KEY_LARGE_ICON, largeIcon);
|
||||
builder.setContentTitle(notification.extras.getString(Notification.EXTRA_TITLE));
|
||||
builder.setContentText(notification.extras.getString(Notification.EXTRA_TEXT));
|
||||
builder.setAutoCancel(0 != (notification.flags & Notification.FLAG_AUTO_CANCEL));
|
||||
if (notification.number >= 0)
|
||||
builder.setNumber(notification.number);
|
||||
String bigText = notification.extras.getString(Notification.EXTRA_BIG_TEXT);
|
||||
if (bigText != null)
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(bigText));
|
||||
|
||||
builder.setWhen(notification.when);
|
||||
String group = notification.getGroup();
|
||||
if (group != null && !group.isEmpty())
|
||||
builder.setGroup(group);
|
||||
builder.setGroupSummary(0 != (notification.flags & Notification.FLAG_GROUP_SUMMARY));
|
||||
String sortKey = notification.getSortKey();
|
||||
if (sortKey != null && !sortKey.isEmpty())
|
||||
builder.setSortKey(sortKey);
|
||||
builder.setShowWhen(notification.extras.getBoolean(Notification.EXTRA_SHOW_WHEN, false));
|
||||
Integer color = UnityNotificationManager.getNotificationColor(notification);
|
||||
if (color != null)
|
||||
UnityNotificationManager.setNotificationColor(builder, color);
|
||||
UnityNotificationManager.setNotificationUsesChronometer(builder, notification.extras.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false));
|
||||
UnityNotificationManager.setNotificationGroupAlertBehavior(builder, UnityNotificationManager.getNotificationGroupAlertBehavior(notification));
|
||||
|
||||
builder.getExtras().putInt(KEY_ID, notification.extras.getInt(KEY_ID, 0));
|
||||
builder.getExtras().putLong(KEY_REPEAT_INTERVAL, notification.extras.getLong(KEY_REPEAT_INTERVAL, 0));
|
||||
builder.getExtras().putLong(KEY_FIRE_TIME, notification.extras.getLong(KEY_FIRE_TIME, 0));
|
||||
String intentData = notification.extras.getString(KEY_INTENT_DATA);
|
||||
if (intentData != null && !intentData.isEmpty())
|
||||
builder.getExtras().putString(KEY_INTENT_DATA, intentData);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Unity.Notifications.Android",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Android",
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac145e6b8c6034cdbadc8c6e26aedbcf
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2928de7141e034d699719cb63e6156e9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Assets/MYp0ZVTT2QSDK/ThirdParty/com.unity.mobile.notifications@2.3.2/Runtime/Unified/Notification.cs
Vendored
+222
@@ -0,0 +1,222 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using PlatformNotification = Unity.Notifications.Android.AndroidNotification;
|
||||
#else
|
||||
using System.Globalization;
|
||||
using PlatformNotification = Unity.Notifications.iOS.iOSNotification;
|
||||
#endif
|
||||
|
||||
namespace Unity.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a notification to be sent or a received one.
|
||||
/// Can be converted to platform specific notification via explicit cast.
|
||||
/// <code>
|
||||
/// var n1 = (AndroidNotification)notification; // convert to Android
|
||||
/// var n1 = (iOSNotification)notification; // convert to iOS
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public struct Notification
|
||||
{
|
||||
PlatformNotification notification;
|
||||
|
||||
public static explicit operator PlatformNotification(Notification n)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
n.notification.ShowInForeground = n.ShowInForeground;
|
||||
n.notification.ShouldAutoCancel = true; // iOS always auto-cancels
|
||||
return n.notification;
|
||||
#else
|
||||
var ret = n.notification;
|
||||
if (ret != null && n.Identifier.HasValue)
|
||||
ret.Identifier = n.Identifier.Value.ToString(CultureInfo.InvariantCulture);
|
||||
ret.ShowInForeground = n.ShowInForeground;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal Notification(PlatformNotification notification
|
||||
#if UNITY_ANDROID
|
||||
, int id
|
||||
#endif
|
||||
)
|
||||
{
|
||||
this.notification = notification;
|
||||
Identifier = default;
|
||||
ShowInForeground = notification.ShowInForeground;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
Identifier = id;
|
||||
#else
|
||||
if (int.TryParse(notification.Identifier, NumberStyles.None, CultureInfo.InvariantCulture, out int val))
|
||||
Identifier = val;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_ANDROID // so code compiles when something other than Android/iOS is selected
|
||||
PlatformNotification GetNotification()
|
||||
{
|
||||
if (notification == null)
|
||||
notification = new PlatformNotification();
|
||||
return notification;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A unique identifier for this notification.
|
||||
/// If null, a unique ID will be generated when scheduling.
|
||||
/// </summary>
|
||||
public int? Identifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// String that is shown on notification as title.
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_IOS
|
||||
if (notification == null)
|
||||
return null;
|
||||
#endif
|
||||
return notification.Title;
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_IOS
|
||||
if (notification == null)
|
||||
notification = new PlatformNotification();
|
||||
#endif
|
||||
notification.Title = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String that is shown on notification as it's main body.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return notification.Text;
|
||||
#else
|
||||
return notification?.Body;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.Text = value;
|
||||
#else
|
||||
GetNotification().Body = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arbitrary data that is sent with notification.
|
||||
/// Can be used to store some useful information in the notification to be later retrieved when notification arrives or is tapped by user.
|
||||
/// </summary>
|
||||
public string Data
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return notification.IntentData;
|
||||
#else
|
||||
return notification?.Data;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.IntentData = value;
|
||||
#else
|
||||
GetNotification().Data = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number, associated with the notification. Zero is ignored.
|
||||
/// When supported, shows up as badge on application launcher.
|
||||
/// </summary>
|
||||
public int Badge
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return notification.Number;
|
||||
#else
|
||||
return notification == null ? 0 : notification.Badge;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.Number = value;
|
||||
#else
|
||||
GetNotification().Badge = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Default value differs on Android/iOS, so have separate here and unify uppon conversion
|
||||
/// <summary>
|
||||
/// Indicated, whether notification should be shown if it arrives while application is in foreground.
|
||||
/// When notification arrives with app in foreground <see cref="NotificationCenter.OnNotificationReceived"/> even fires regardless of this.
|
||||
/// Default is false, meaning notifications are silent when app is in foreground.
|
||||
/// </summary>
|
||||
public bool ShowInForeground { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier for the group this notification belong to.
|
||||
/// If device supports it, notifications with same group identifier are stacked together.
|
||||
/// On Android this is also called group, while on iOS it is called thread identidier.
|
||||
/// </summary>
|
||||
public string Group
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return notification.Group;
|
||||
#else
|
||||
return notification?.ThreadIdentifier;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.Group = value;
|
||||
#else
|
||||
GetNotification().ThreadIdentifier = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this notification as group summary.
|
||||
/// Only has effect if <see cref="Group"/> is also set.
|
||||
/// Android only. On iOS will be just another notification in the group.
|
||||
/// </summary>
|
||||
public bool IsGroupSummary
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return notification.GroupSummary;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.GroupSummary = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c21b2456627d40629aab46fbb054de9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using Unity.Notifications.Android;
|
||||
#else
|
||||
using System.Globalization;
|
||||
using Unity.Notifications.iOS;
|
||||
#endif
|
||||
|
||||
namespace Unity.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Options, specifying how notifications should be presented to the user.
|
||||
/// These option can be bitwise-ored to combine them.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On Android Alert and Sound flags are used to choose importance level for the channel, so specifying Alert also includes sound.
|
||||
/// </remarks>
|
||||
/// <seealso cref="NotificationCenterArgs.PresentationOptions"/>
|
||||
[Flags]
|
||||
public enum NotificationPresentation
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that when notification arrives, a pop-up should show up on screen.
|
||||
/// Alerts can be disabled by user in device settings. They may also be disabled by default.
|
||||
/// </summary>
|
||||
Alert = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Whether notifications can set a badge on applications launcher.
|
||||
/// </summary>
|
||||
Badge = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Whether notifications cause device to play sound uppon arrival.
|
||||
/// </summary>
|
||||
Sound = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Causes device to vibrate when notification is received. Android only.
|
||||
/// </summary>
|
||||
Vibrate = 1 << 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The settings section to open, if possible.
|
||||
/// </summary>
|
||||
public enum NotificationSettingsSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens settings for application and tries to open the section for notifications.
|
||||
/// </summary>
|
||||
Application,
|
||||
|
||||
/// <summary>
|
||||
/// Tries to navigate to section for a particular notification category.
|
||||
/// Since Android 8.0 will open notification settings for the specific notification channel.
|
||||
/// </summary>
|
||||
Category,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialization arguments for <see cref="NotificationCenter"/>.
|
||||
/// Recommended to use <see cref="Default"/> to retrieve recommened default values and then alter it.
|
||||
/// It is required to manually set <see cref="AndroidChannelId"/>.
|
||||
/// </summary>
|
||||
public struct NotificationCenterArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns recommended default values for all settings.
|
||||
/// </summary>
|
||||
public static NotificationCenterArgs Default => new NotificationCenterArgs()
|
||||
{
|
||||
PresentationOptions = NotificationPresentation.Badge | NotificationPresentation.Sound,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Options specifying how notifications should be presented to user when they arrive.
|
||||
/// On Android these only have effect if notification channel is created uppon initialization (all channel related properties are set).
|
||||
/// On iOS these only have effect if permission to post notification is later requested using <see cref="NotificationCenter.RequestPermission"/>
|
||||
/// </summary>
|
||||
public NotificationPresentation PresentationOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A custom non-empty string to identify notification channel. Required, Android only.
|
||||
/// All notifications will be sent to this channel.
|
||||
/// Channel is created automatically during initialization if <see cref="AndroidChannelName"/> and <see cref="AndroidChannelDescription"/> are both set.
|
||||
/// If name and description are left null, channel with given identifier has to be created manually (for example using <see cref="AndroidNotificationCenter.RegisterNotificationChannel(AndroidNotificationChannel)"/>).
|
||||
/// </summary>
|
||||
public string AndroidChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A user visible name for notification channel. Optional, Android only.
|
||||
/// Leave null, if you wish to create channel manually.
|
||||
/// Set this to channel name for it to be created automatically during initialization.
|
||||
/// If this is set, then <see cref="AndroidChannelDescription"/> must also be set.
|
||||
/// </summary>
|
||||
/// <seealso cref="AndroidChannelId"/>
|
||||
public string AndroidChannelName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A user visible description for the channel. Optional, Android only.
|
||||
/// Leave null, if you wish to create channel manually.
|
||||
/// Set this to channel description for it to be created automatically during initialization.
|
||||
/// If this is set, then <see cref="AndroidChannelName"/> must also be set.
|
||||
/// </summary>
|
||||
/// <seealso cref="AndroidChannelId"/>
|
||||
public string AndroidChannelDescription { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send, receive and manage notifications.
|
||||
/// Must be initialized before use. See <see cref="Initialize(NotificationCenterArgs)"/>.
|
||||
/// </summary>
|
||||
public static class NotificationCenter
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for <see cref="OnNotificationReceived"/>.
|
||||
/// </summary>
|
||||
/// <param name="notification">Notification that has been received.</param>
|
||||
public delegate void NotificationReceivedCallback(Notification notification);
|
||||
|
||||
static bool s_Initialized = false;
|
||||
static NotificationCenterArgs s_Args;
|
||||
static event NotificationReceivedCallback s_OnNotificationReceived;
|
||||
static bool s_OnNotificationReceivedSet = false;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
static void NotificationReceived(AndroidNotificationIntentData data)
|
||||
#else
|
||||
static void NotificationReceived(iOSNotification notif)
|
||||
#endif
|
||||
{
|
||||
if (s_OnNotificationReceived != null)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var notification = new Notification(data.Notification, data.Id);
|
||||
#else
|
||||
var notification = new Notification(notif);
|
||||
#endif
|
||||
|
||||
s_OnNotificationReceived(notification);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires when notification is received.
|
||||
/// Application must be running for this event to work.
|
||||
/// On Android this even fires if application is in foreground or when it's brought back from background.
|
||||
/// On iOS this even only fires when notification is received while app is in foreground.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event is the same as AndroidNotificationCenter.OnNotificationReceived and iOSNotificationCenter.OnNotificationReceived.
|
||||
/// </remarks>
|
||||
public static event NotificationReceivedCallback OnNotificationReceived
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!s_OnNotificationReceivedSet)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.OnNotificationReceived += NotificationReceived;
|
||||
#else
|
||||
iOSNotificationCenter.OnNotificationReceived += NotificationReceived;
|
||||
#endif
|
||||
s_OnNotificationReceivedSet = true;
|
||||
}
|
||||
s_OnNotificationReceived += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
s_OnNotificationReceived -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes notification center with given arguments.
|
||||
/// On Android it will also create notification channel if arguments are set accordingly.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments for initialization.</param>
|
||||
public static void Initialize(NotificationCenterArgs args)
|
||||
{
|
||||
if (s_Initialized)
|
||||
return;
|
||||
if (string.IsNullOrEmpty(args.AndroidChannelId))
|
||||
throw new ArgumentException("AndroidChannel not provided");
|
||||
|
||||
s_Args = args;
|
||||
s_Initialized = true;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.Initialize();
|
||||
if (args.AndroidChannelName != null || args.AndroidChannelDescription != null)
|
||||
{
|
||||
Importance importance = Importance.Low;
|
||||
if (0 != (args.PresentationOptions & NotificationPresentation.Alert))
|
||||
importance = Importance.High;
|
||||
else if (0 != (args.PresentationOptions & NotificationPresentation.Sound))
|
||||
importance = Importance.Default;
|
||||
|
||||
AndroidNotificationCenter.RegisterNotificationChannel(new AndroidNotificationChannel()
|
||||
{
|
||||
Id = args.AndroidChannelId,
|
||||
Name = args.AndroidChannelName,
|
||||
Description = args.AndroidChannelDescription,
|
||||
Importance = importance,
|
||||
CanShowBadge = 0 != (args.PresentationOptions & NotificationPresentation.Badge),
|
||||
EnableVibration = 0 != (args.PresentationOptions & NotificationPresentation.Vibrate),
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request for users permission to post notifications.
|
||||
/// Requests users permission to send notifications. Unless already allowed or permanently denied, shows UI to the user.
|
||||
/// Users can revoke permission in Settings while app is not running.
|
||||
/// </summary>
|
||||
/// <returns>An object for tracking the request and obtaining results.</returns>
|
||||
/// <remarks>Before Android 13 no permission is required.</remarks>
|
||||
public static NotificationsPermissionRequest RequestPermission()
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
int iOSAuthorizationOptions = 0;
|
||||
#if UNITY_IOS
|
||||
if (0 != (s_Args.PresentationOptions & NotificationPresentation.Alert))
|
||||
iOSAuthorizationOptions |= (int)AuthorizationOption.Alert;
|
||||
if (0 != (s_Args.PresentationOptions & NotificationPresentation.Badge))
|
||||
iOSAuthorizationOptions |= (int)AuthorizationOption.Badge;
|
||||
if (0 != (s_Args.PresentationOptions & NotificationPresentation.Sound))
|
||||
iOSAuthorizationOptions |= (int)AuthorizationOption.Sound;
|
||||
#endif
|
||||
return new NotificationsPermissionRequest(iOSAuthorizationOptions);
|
||||
}
|
||||
|
||||
static void CheckInitialized()
|
||||
{
|
||||
if (!s_Initialized)
|
||||
throw new Exception("NotificationCenter not initialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule notification to be shown in the future.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the schedule, usually deduced from actually passed one.</typeparam>
|
||||
/// <param name="notification">Notification to send.</param>
|
||||
/// <param name="schedule">Schedule, specifying, when notification should be shown.</param>
|
||||
/// <returns>Notification identifier.</returns>
|
||||
public static int ScheduleNotification<T>(Notification notification, T schedule)
|
||||
where T : NotificationSchedule
|
||||
{
|
||||
string category = null;
|
||||
#if UNITY_ANDROID
|
||||
category = s_Args.AndroidChannelId;
|
||||
#endif
|
||||
return ScheduleNotification(notification, category, schedule);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule notification to be shown in the future.
|
||||
/// Allows to explicitly specify the category to send notification to. On Android it is notification channel.
|
||||
/// Channel or category has to be created manually using AndroidNotificationCenter and iOSNotificationCenter respectively.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the schedule, usually deduced from actually passed one.</typeparam>
|
||||
/// <param name="notification">Notification to send.</param>
|
||||
/// <param name="category">Identifier for iOS category or Android channel.</param>
|
||||
/// <param name="schedule">Schedule, specifying, when notification should be shown.</param>
|
||||
/// <returns>Notification identifier.</returns>
|
||||
public static int ScheduleNotification<T>(Notification notification, string category, T schedule)
|
||||
where T : NotificationSchedule
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
var n = (AndroidNotification)notification;
|
||||
schedule.Schedule(ref n);
|
||||
if (notification.Identifier.HasValue)
|
||||
{
|
||||
AndroidNotificationCenter.SendNotificationWithExplicitID(n, category, notification.Identifier.Value);
|
||||
return notification.Identifier.Value;
|
||||
}
|
||||
else
|
||||
return AndroidNotificationCenter.SendNotification(n, category);
|
||||
#else
|
||||
var n = (iOSNotification)notification;
|
||||
if (n == null)
|
||||
throw new ArgumentException("Passed notifiation is empty");
|
||||
n.CategoryIdentifier = category;
|
||||
schedule.Schedule(ref n);
|
||||
iOSNotificationCenter.ScheduleNotification(n);
|
||||
if (notification.Identifier.HasValue)
|
||||
return notification.Identifier.Value;
|
||||
// iOSNotification is class and has auto-generated id set at this point
|
||||
// for consistency with Android set it back to null, so same Notification can be sent again as new one
|
||||
int id = int.Parse(n.Identifier, NumberStyles.None, CultureInfo.InvariantCulture);
|
||||
n.Identifier = null;
|
||||
return id;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns last notification tapped by user, or null.
|
||||
/// </summary>
|
||||
public static Notification? LastRespondedNotification
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
var intent = AndroidNotificationCenter.GetLastNotificationIntent();
|
||||
if (intent == null)
|
||||
return null;
|
||||
return new Notification(intent.Notification, intent.Id);
|
||||
#else
|
||||
var notification = iOSNotificationCenter.GetLastRespondedNotification();
|
||||
if (notification == null)
|
||||
return null;
|
||||
return new Notification(notification);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel a scheduled notification.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the notification to cancel.</param>
|
||||
public static void CancelScheduledNotification(int id)
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.CancelScheduledNotification(id);
|
||||
#else
|
||||
iOSNotificationCenter.RemoveScheduledNotification(id.ToString(CultureInfo.InvariantCulture));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel delivered notification.
|
||||
/// Removes notification from the tray.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the notification to cancel.</param>
|
||||
public static void CancelDeliveredNotification(int id)
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.CancelDisplayedNotification(id);
|
||||
#else
|
||||
iOSNotificationCenter.RemoveDeliveredNotification(id.ToString(CultureInfo.InvariantCulture));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all future notifications.
|
||||
/// </summary>
|
||||
public static void CancelAllScheduledNotifications()
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.CancelAllScheduledNotifications();
|
||||
#else
|
||||
iOSNotificationCenter.RemoveAllScheduledNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all already delivered notifications.
|
||||
/// </summary>
|
||||
public static void CancelAllDeliveredNotifications()
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
AndroidNotificationCenter.CancelAllDisplayedNotifications();
|
||||
#else
|
||||
iOSNotificationCenter.RemoveAllDeliveredNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear Application badge. iOS only.
|
||||
/// iOS applications can set numeric badge on app icon. Calling this method removes that badge.
|
||||
/// On Android badge is removed automatically when notifications are removed.
|
||||
/// </summary>
|
||||
public static void ClearBadge()
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_IOS
|
||||
iOSNotificationCenter.ApplicationBadge = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens settings for the application.
|
||||
/// If possible, will try to navigate as close to requested section as it can.
|
||||
/// On iOS and Android prior to 8.0 will open settings for the application.
|
||||
/// Since Android 8.0 will open notification settings for either application or the default channel.
|
||||
/// </summary>
|
||||
/// <param name="section">The section to navigate to.</param>
|
||||
public static void OpenNotificationSettings(NotificationSettingsSection section = NotificationSettingsSection.Application)
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
#if UNITY_ANDROID
|
||||
string channel = section switch
|
||||
{
|
||||
NotificationSettingsSection.Category => s_Args.AndroidChannelId,
|
||||
NotificationSettingsSection.Application => null,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
AndroidNotificationCenter.OpenNotificationSettings(channel);
|
||||
#else
|
||||
iOSNotificationCenter.OpenNotificationSettings();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89f50368f285b453ebeac8f965391651
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
#if UNITY_ANDROID
|
||||
using PlatformNotification = Unity.Notifications.Android.AndroidNotification;
|
||||
#else
|
||||
using Unity.Notifications.iOS;
|
||||
using PlatformNotification = Unity.Notifications.iOS.iOSNotification;
|
||||
#endif
|
||||
|
||||
namespace Unity.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Interval, at which notification should repeat.
|
||||
/// </summary>
|
||||
public enum NotificationRepeatInterval
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates, that notification does not repeat.
|
||||
/// </summary>
|
||||
OneTime = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates, that notification should repeat daily.
|
||||
/// When used in <see cref="NotificationDateTimeSchedule"/>, only time is used for scheduling.
|
||||
/// </summary>
|
||||
Daily = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for different schedule types.
|
||||
/// </summary>
|
||||
public interface NotificationSchedule
|
||||
{
|
||||
internal void Schedule(ref PlatformNotification notification);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule notification to show up after a certain amount of time, optionally repeating at the same time interval.
|
||||
/// </summary>
|
||||
public struct NotificationIntervalSchedule
|
||||
: NotificationSchedule
|
||||
{
|
||||
/// <summary>
|
||||
/// Time interval to show notification from current time.
|
||||
/// Only full seconds are considered.
|
||||
/// </summary>
|
||||
public TimeSpan Interval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether notification should repeat.
|
||||
/// If true, notification will repeat at the same interval as initial time from the current one.
|
||||
/// </summary>
|
||||
public bool Repeats { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convenience constructor.
|
||||
/// </summary>
|
||||
/// <param name="interval">Value for <see cref="Interval"/></param>
|
||||
/// <param name="repeats">Value for <see cref="Repeats"/></param>
|
||||
public NotificationIntervalSchedule(TimeSpan interval, bool repeats = false)
|
||||
{
|
||||
Interval = interval;
|
||||
Repeats = repeats;
|
||||
}
|
||||
|
||||
void NotificationSchedule.Schedule(ref PlatformNotification notification)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
notification.FireTime = DateTime.Now + Interval;
|
||||
if (Repeats)
|
||||
notification.RepeatInterval = Interval;
|
||||
#else
|
||||
notification.Trigger = new iOSNotificationTimeIntervalTrigger()
|
||||
{
|
||||
TimeInterval = Interval,
|
||||
Repeats = Repeats,
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule to show notification at particular date and time.
|
||||
/// Optionally can repeat at predefined intervals.
|
||||
/// </summary>
|
||||
public struct NotificationDateTimeSchedule
|
||||
: NotificationSchedule
|
||||
{
|
||||
/// <summary>
|
||||
/// Date and time when notification has to be shown if does not repeat.
|
||||
/// If notification is set to repeat, the meaning of this value depends on <see cref="NotificationRepeatInterval"/>.
|
||||
/// </summary>
|
||||
public DateTime FireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval, at which notification should repeat from the first delivery.
|
||||
/// </summary>
|
||||
public NotificationRepeatInterval RepeatInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convenience constructor.
|
||||
/// </summary>
|
||||
/// <param name="fireTime">Value for <see cref="FireTime"/></param>
|
||||
/// <param name="repeatInterval">Value for <see cref="RepeatInterval"/></param>
|
||||
public NotificationDateTimeSchedule(DateTime fireTime, NotificationRepeatInterval repeatInterval = NotificationRepeatInterval.OneTime)
|
||||
{
|
||||
FireTime = fireTime;
|
||||
RepeatInterval = repeatInterval;
|
||||
}
|
||||
|
||||
void NotificationSchedule.Schedule(ref PlatformNotification notification)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
// TODO handle UTC
|
||||
switch (RepeatInterval)
|
||||
{
|
||||
case NotificationRepeatInterval.OneTime:
|
||||
notification.FireTime = FireTime;
|
||||
break;
|
||||
case NotificationRepeatInterval.Daily:
|
||||
{
|
||||
var currentTime = DateTime.Now;
|
||||
var fireTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, FireTime.Hour, FireTime.Minute, FireTime.Second);
|
||||
if (fireTime < currentTime)
|
||||
fireTime = fireTime.AddDays(1);
|
||||
notification.FireTime = fireTime;
|
||||
notification.RepeatInterval = TimeSpan.FromDays(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
var trigger = new iOSNotificationCalendarTrigger()
|
||||
{
|
||||
Hour = FireTime.Hour,
|
||||
Minute = FireTime.Minute,
|
||||
Second = FireTime.Second,
|
||||
UtcTime = FireTime.Kind == DateTimeKind.Utc,
|
||||
};
|
||||
|
||||
switch (RepeatInterval)
|
||||
{
|
||||
case NotificationRepeatInterval.OneTime:
|
||||
trigger.Year = FireTime.Year;
|
||||
trigger.Month = FireTime.Month;
|
||||
trigger.Day = FireTime.Day;
|
||||
break;
|
||||
case NotificationRepeatInterval.Daily:
|
||||
trigger.Day = null;
|
||||
trigger.Repeats = true;
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unsupported repeat interval {RepeatInterval}");
|
||||
}
|
||||
|
||||
notification.Trigger = trigger;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 304ff37de66064f79ad81cb7991a4b36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using Unity.Notifications.Android;
|
||||
#else
|
||||
using Unity.Notifications.iOS;
|
||||
#endif
|
||||
|
||||
namespace Unity.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// The status of notification permission request.
|
||||
/// </summary>
|
||||
/// <seealso cref="NotificationsPermissionRequest.Status"/>
|
||||
public enum NotificationsPermissionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that request is ongoing. Usually mean that user is presented with UI to respond to.
|
||||
/// </summary>
|
||||
RequestPending,
|
||||
|
||||
/// <summary>
|
||||
/// Permission granted, you can post notifications.
|
||||
/// </summary>
|
||||
Granted,
|
||||
|
||||
/// <summary>
|
||||
/// Permission denied, sending notifications will not work.
|
||||
/// </summary>
|
||||
Denied,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Track the status of notification permission request.
|
||||
/// Can be returned from coroutine to suspend it until request is either granted or denied.
|
||||
/// Permission can be granted or denied immediately, if this isn't the first request.
|
||||
/// </summary>
|
||||
public class NotificationsPermissionRequest
|
||||
: CustomYieldInstruction
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
PermissionRequest request;
|
||||
#else
|
||||
AuthorizationRequest request;
|
||||
#endif
|
||||
|
||||
internal NotificationsPermissionRequest(int options)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
// do not create request if already allowed
|
||||
if (AndroidNotificationCenter.UserPermissionToPost != PermissionStatus.Allowed)
|
||||
request = new PermissionRequest();
|
||||
#else
|
||||
request = new AuthorizationRequest((AuthorizationOption)options, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden property of base class. Indicates if coroutine should be suspended.
|
||||
/// </summary>
|
||||
public override bool keepWaiting => (request == null)
|
||||
? false
|
||||
#if UNITY_ANDROID
|
||||
: request.Status == PermissionStatus.RequestPending;
|
||||
#else
|
||||
: !request.IsFinished;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a status of this request.
|
||||
/// </summary>
|
||||
public NotificationsPermissionStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (request == null)
|
||||
return NotificationsPermissionStatus.Granted;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
return request.Status switch
|
||||
{
|
||||
PermissionStatus.RequestPending => NotificationsPermissionStatus.RequestPending,
|
||||
PermissionStatus.Allowed => NotificationsPermissionStatus.Granted,
|
||||
_ => NotificationsPermissionStatus.Denied,
|
||||
};
|
||||
#else
|
||||
if (!request.IsFinished)
|
||||
return NotificationsPermissionStatus.RequestPending;
|
||||
return request.Granted ? NotificationsPermissionStatus.Granted : NotificationsPermissionStatus.Denied;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf48a9bb20f8b4cdbbf92393fd680077
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Unity.Notifications.Unified",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.Notifications.Android",
|
||||
"Unity.Notifications.iOS"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Android",
|
||||
"Editor",
|
||||
"iOS"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d71139be5ab9e4bb49bc95f8093014e9
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00c2f1475f1804a4e8559419f485be7b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.Notifications.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.iOS.Notifications.Tests")]
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaec56e278645461f8385f7af3c99e8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum for requesting authorization to interact with the user.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthorizationOption
|
||||
{
|
||||
/// <summary>
|
||||
/// The ability to update the app’s badge.
|
||||
/// </summary>
|
||||
Badge = (1 << 0),
|
||||
|
||||
/// <summary>
|
||||
/// The ability to play sounds.
|
||||
/// </summary>
|
||||
Sound = (1 << 1),
|
||||
|
||||
/// <summary>
|
||||
/// The ability to display alerts.
|
||||
/// </summary>
|
||||
Alert = (1 << 2),
|
||||
|
||||
/// <summary>
|
||||
/// The ability to display notifications in a CarPlay environment.
|
||||
/// </summary>
|
||||
CarPlay = (1 << 3),
|
||||
|
||||
/// <summary>
|
||||
/// The ability to play sounds for critical alerts.
|
||||
/// Critical alerts require a special entitlement issued by Apple.
|
||||
/// </summary>
|
||||
CriticalAlert = (1 << 4),
|
||||
|
||||
/// <summary>
|
||||
/// An option indicating the system should display a button for in-app notification settings.
|
||||
/// </summary>
|
||||
ProvidesAppNotificationSettings = (1 << 5),
|
||||
|
||||
/// <summary>
|
||||
/// The ability to post noninterrupting notifications provisionally to the Notification Center.
|
||||
/// </summary>
|
||||
Provisional = (1 << 6),
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct iOSAuthorizationRequestData
|
||||
{
|
||||
internal int granted;
|
||||
internal string error;
|
||||
internal string deviceToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this to request authorization to interact with the user when you with to deliver local and remote notifications are delivered to the user's device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method must be called before you attempt to schedule any local notifications. If "Request Authorization on App Launch" is enabled in
|
||||
/// "Edit -> Project Settings -> Mobile Notification Settings" this method will be called automatically when the app launches. You might call this method again to determine the current
|
||||
/// authorizations status or retrieve the DeviceToken for Push Notifications. However the UI system prompt will not be shown if the user has already granted or denied authorization for this app.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using (var req = new AuthorizationRequest(AuthorizationOption.Alert | AuthorizationOption.Badge, true))
|
||||
/// {
|
||||
/// while (!req.IsFinished)
|
||||
/// {
|
||||
/// yield return null;
|
||||
/// };
|
||||
///
|
||||
/// string result = "\n RequestAuthorization: \n";
|
||||
/// result += "\n finished: " + req.IsFinished;
|
||||
/// result += "\n granted : " + req.Granted;
|
||||
/// result += "\n error: " + req.Error;
|
||||
/// result += "\n deviceToken: " + req.DeviceToken;
|
||||
/// Debug.Log(res);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class AuthorizationRequest : IDisposable
|
||||
{
|
||||
bool m_IsFinished;
|
||||
bool m_Granted;
|
||||
string m_Error;
|
||||
string m_DeviceToken;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the authorization request has completed.
|
||||
/// </summary>
|
||||
public bool IsFinished
|
||||
{
|
||||
get { lock (this) { return m_IsFinished; } }
|
||||
private set { m_IsFinished = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A property indicating whether authorization was granted. The value of this parameter is set to true when authorization was granted for one or more options. The value is set to false when authorization is denied for all options.
|
||||
/// </summary>
|
||||
public bool Granted
|
||||
{
|
||||
get { lock (this) { return m_Granted; } }
|
||||
private set { m_Granted = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains error information of the request failed for some reason or an empty string if no error occurred.
|
||||
/// </summary>
|
||||
public string Error
|
||||
{
|
||||
get { lock (this) { return m_Error; } }
|
||||
private set { m_Error = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A globally unique token that identifies this device to Apple Push Notification Network. Send this token to the server that you use to generate remote notifications.
|
||||
/// Your server must pass this token unmodified back to APNs when sending those remote notifications.
|
||||
/// This property will be empty if you set the registerForRemoteNotifications parameter to false when creating the Authorization request or if the app fails registration with the APN.
|
||||
/// </summary>
|
||||
public string DeviceToken
|
||||
{
|
||||
get { lock (this) { return m_DeviceToken; } }
|
||||
private set { m_DeviceToken = value; }
|
||||
}
|
||||
|
||||
static AuthorizationRequest()
|
||||
{
|
||||
iOSNotificationsWrapper.RegisterAuthorizationRequestCallback();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiate an authorization request.
|
||||
/// </summary>
|
||||
/// <param name="authorizationOption"> The authorization options your app is requesting. You may specify multiple options to request authorization for. Request only the authorization options that you plan to use.</param>
|
||||
/// <param name="registerForRemoteNotifications"> Set this to true to initiate the registration process with Apple Push Notification service after the user has granted authorization
|
||||
/// If registration succeeds the DeviceToken will be returned. You should pass this token along to the server you use to generate remote notifications for the device. </param>
|
||||
public AuthorizationRequest(AuthorizationOption authorizationOption, bool registerForRemoteNotifications)
|
||||
{
|
||||
var handle = GCHandle.Alloc(this);
|
||||
iOSNotificationsWrapper.RequestAuthorization(GCHandle.ToIntPtr(handle), (int)authorizationOption, registerForRemoteNotifications);
|
||||
}
|
||||
|
||||
private void OnAuthorizationRequestCompleted(iOSAuthorizationRequestData requestData)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
IsFinished = true;
|
||||
Granted = requestData.granted != 0;
|
||||
Error = requestData.error;
|
||||
DeviceToken = requestData.deviceToken;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void OnAuthorizationRequestCompleted(IntPtr request, iOSAuthorizationRequestData requestData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handle = GCHandle.FromIntPtr(request);
|
||||
var req = handle.Target as AuthorizationRequest;
|
||||
handle.Free();
|
||||
req.OnAuthorizationRequestCompleted(requestData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the app is currently registered for remote notifications.
|
||||
/// </summary>
|
||||
/// <seealso cref="https://developer.apple.com/documentation/uikit/uiapplication/1623069-registeredforremotenotifications?language=objc"/>
|
||||
public static bool RegisteredForRemoteNotifications
|
||||
{
|
||||
get => iOSNotificationsWrapper.RegisteredForRemoteNotifications();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister for remote notifications.
|
||||
/// This is rarely needed, typically when user logs out. The app can always re-register.
|
||||
/// </summary>
|
||||
/// <seealso cref="https://developer.apple.com/documentation/uikit/uiapplication/1623093-unregisterforremotenotifications?language=objc"/>
|
||||
public static void UnregisterForRemoteNotifications()
|
||||
{
|
||||
iOSNotificationsWrapper.UnregisterForRemoteNotifications();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose to unregister the OnAuthorizationRequestCompleted callback.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b313ac86fae05a14cb6f9c95468075e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c6a1338bb05c4940b3099839651a262
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// UnityAppController+Notifications.h
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "UnityAppController.h"
|
||||
|
||||
#include "Classes/PluginBase/LifeCycleListener.h"
|
||||
#include "Classes/PluginBase/AppDelegateListener.h"
|
||||
|
||||
@interface UnityAppController (Notifications)
|
||||
|
||||
@end
|
||||
|
||||
@interface UnityNotificationLifeCycleManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16485d365daaf42668d353cf0a47af00
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude iOS: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// UnityAppController+Notifications.m
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "UnityNotificationManager.h"
|
||||
#import "UnityAppController+Notifications.h"
|
||||
|
||||
@implementation UnityNotificationLifeCycleManager
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
[UnityNotificationLifeCycleManager sharedInstance];
|
||||
});
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
{
|
||||
static UnityNotificationLifeCycleManager *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[UnityNotificationLifeCycleManager alloc] init];
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[nc addObserverForName: UIApplicationDidBecomeActiveNotification
|
||||
object: nil
|
||||
queue: [NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager updateScheduledNotificationList];
|
||||
[manager updateDeliveredNotificationList];
|
||||
[manager updateNotificationSettings];
|
||||
}];
|
||||
|
||||
[nc addObserverForName: UIApplicationDidEnterBackgroundNotification
|
||||
object: nil
|
||||
queue: [NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
[UnityNotificationManager sharedInstance].lastReceivedNotification = NULL;
|
||||
}];
|
||||
|
||||
[nc addObserverForName: kUnityWillFinishLaunchingWithOptions
|
||||
object: nil
|
||||
queue: [NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = [UnityNotificationManager sharedInstance];
|
||||
|
||||
BOOL authorizeOnLaunch = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityNotificationRequestAuthorizationOnAppLaunch"] boolValue];
|
||||
BOOL supportsPushNotification = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityAddRemoteNotificationCapability"] boolValue];
|
||||
BOOL registerRemoteOnLaunch = supportsPushNotification == YES ?
|
||||
[[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityNotificationRequestAuthorizationForRemoteNotificationsOnAppLaunch"] boolValue] : NO;
|
||||
|
||||
NSInteger defaultAuthorizationOptions = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityNotificationDefaultAuthorizationOptions"] integerValue];
|
||||
|
||||
if (defaultAuthorizationOptions <= 0)
|
||||
defaultAuthorizationOptions = (UNAuthorizationOptionSound + UNAuthorizationOptionAlert + UNAuthorizationOptionBadge);
|
||||
|
||||
if (authorizeOnLaunch)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager requestAuthorization: defaultAuthorizationOptions withRegisterRemote: registerRemoteOnLaunch forRequest: NULL];
|
||||
}
|
||||
}];
|
||||
|
||||
[nc addObserverForName: kUnityDidRegisterForRemoteNotificationsWithDeviceToken
|
||||
object: nil
|
||||
queue: [NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken");
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager finishRemoteNotificationRegistration: UNAuthorizationStatusAuthorized notification: notification];
|
||||
}];
|
||||
|
||||
[nc addObserverForName: kUnityDidFailToRegisterForRemoteNotificationsWithError
|
||||
object: nil
|
||||
queue: [NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
NSLog(@"didFailToRegisterForRemoteNotificationsWithError");
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager finishRemoteNotificationRegistration: UNAuthorizationStatusDenied notification: notification];
|
||||
}];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1fc929ae85b245fcadbb125623bc498
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
tvOS: tvOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// UnityNotificationData.h
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#ifndef UnityNotificationData_h
|
||||
#define UnityNotificationData_h
|
||||
|
||||
enum triggerType
|
||||
{
|
||||
TIME_TRIGGER = 0,
|
||||
CALENDAR_TRIGGER = 10,
|
||||
LOCATION_TRIGGER = 20,
|
||||
PUSH_TRIGGER = 3,
|
||||
UNKNOWN_TRIGGER = -1,
|
||||
};
|
||||
|
||||
enum UnitySoundType
|
||||
{
|
||||
kSoundTypeDefault = 0,
|
||||
kSoundTypeCritical = 1,
|
||||
kSoundTypeRingtone = 2,
|
||||
kSoundTypeNone = 4,
|
||||
};
|
||||
|
||||
enum UnityNotificationInterruptionLevel
|
||||
{
|
||||
kInterruptionLevelActive = 0,
|
||||
kInterruptionLevelCritical = 1,
|
||||
kInterruptionLevelPassive = 2,
|
||||
kInterruptionLevelTimeSensitive = 3,
|
||||
};
|
||||
|
||||
typedef struct iOSNotificationData
|
||||
{
|
||||
char* identifier;
|
||||
char* title;
|
||||
char* body;
|
||||
int badge;
|
||||
char* subtitle;
|
||||
char* categoryIdentifier;
|
||||
char* threadIdentifier;
|
||||
int soundType;
|
||||
float soundVolume;
|
||||
char* soundName;
|
||||
int interruptionLevel;
|
||||
double relevanceScore;
|
||||
|
||||
void* userInfo;
|
||||
void* attachments;
|
||||
|
||||
// Trigger
|
||||
int triggerType; //0 - time, 1 - calendar, 2 - location, 3 - push.
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int interval;
|
||||
unsigned char repeats;
|
||||
} timeInterval;
|
||||
|
||||
struct
|
||||
{
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
unsigned char repeats;
|
||||
} calendar;
|
||||
|
||||
struct
|
||||
{
|
||||
double latitude;
|
||||
double longitude;
|
||||
float radius;
|
||||
unsigned char notifyOnEntry;
|
||||
unsigned char notifyOnExit;
|
||||
unsigned char repeats;
|
||||
} location;
|
||||
} trigger;
|
||||
} iOSNotificationData;
|
||||
|
||||
typedef struct iOSNotificationAuthorizationData
|
||||
{
|
||||
int granted;
|
||||
const char* error;
|
||||
const char* deviceToken;
|
||||
} iOSNotificationAuthorizationData;
|
||||
|
||||
typedef struct NotificationSettingsData
|
||||
{
|
||||
int authorizationStatus;
|
||||
int notificationCenterSetting;
|
||||
int lockScreenSetting;
|
||||
int carPlaySetting;
|
||||
int alertSetting;
|
||||
int badgeSetting;
|
||||
int soundSetting;
|
||||
int alertStyle;
|
||||
int showPreviewsSetting;
|
||||
} NotificationSettingsData;
|
||||
|
||||
typedef void (*NotificationDataReceivedResponse)(iOSNotificationData data);
|
||||
typedef void (*AuthorizationRequestResponse) (void* request, struct iOSNotificationAuthorizationData data);
|
||||
|
||||
NotificationSettingsData UNNotificationSettingsToNotificationSettingsData(UNNotificationSettings* settings);
|
||||
iOSNotificationData UNNotificationRequestToiOSNotificationData(UNNotificationRequest* request);
|
||||
void freeiOSNotificationData(iOSNotificationData* notificationData);
|
||||
|
||||
#endif /* UnityNotificationData_h */
|
||||
|
||||
#endif
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7609257501e8b48f98214be5de026339
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude iOS: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// UnityNotificationData.m
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#if UNITY_USES_LOCATION
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#endif
|
||||
|
||||
#import "UnityNotificationData.h"
|
||||
|
||||
|
||||
static NSString* ParseNotificationDataObject(id obj)
|
||||
{
|
||||
if ([obj isKindOfClass: [NSString class]])
|
||||
return obj;
|
||||
else if ([obj isKindOfClass: [NSNumber class]])
|
||||
{
|
||||
NSNumber* numberVal = obj;
|
||||
if (CFBooleanGetTypeID() == CFGetTypeID((__bridge CFTypeRef)(obj)))
|
||||
return numberVal.boolValue ? @"true" : @"false";
|
||||
return numberVal.stringValue;
|
||||
}
|
||||
else if ([NSJSONSerialization isValidJSONObject: obj])
|
||||
{
|
||||
NSError* error;
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject: obj options: NSJSONWritingPrettyPrinted error: &error];
|
||||
if (data)
|
||||
{
|
||||
NSString* v = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
|
||||
return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Failed parsing notification userInfo value: %@", error);
|
||||
}
|
||||
}
|
||||
else
|
||||
NSLog(@"Failed parsing notification userInfo value");
|
||||
|
||||
NSObject* o = obj;
|
||||
return o.description;
|
||||
}
|
||||
|
||||
NotificationSettingsData UNNotificationSettingsToNotificationSettingsData(UNNotificationSettings* settings)
|
||||
{
|
||||
NotificationSettingsData settingsData;
|
||||
|
||||
settingsData.alertSetting = (int)settings.alertSetting;
|
||||
settingsData.authorizationStatus = (int)settings.authorizationStatus;
|
||||
settingsData.badgeSetting = (int)settings.badgeSetting;
|
||||
settingsData.carPlaySetting = (int)settings.carPlaySetting;
|
||||
settingsData.lockScreenSetting = (int)settings.lockScreenSetting;
|
||||
settingsData.notificationCenterSetting = (int)settings.notificationCenterSetting;
|
||||
settingsData.soundSetting = (int)settings.soundSetting;
|
||||
settingsData.alertStyle = (int)settings.alertStyle;
|
||||
settingsData.showPreviewsSetting = (int)settings.showPreviewsSetting;
|
||||
return settingsData;
|
||||
}
|
||||
|
||||
void initiOSNotificationData(iOSNotificationData* notificationData)
|
||||
{
|
||||
notificationData->title = NULL;
|
||||
notificationData->body = NULL;
|
||||
notificationData->badge = 0;
|
||||
notificationData->subtitle = NULL;
|
||||
notificationData->categoryIdentifier = NULL;
|
||||
notificationData->threadIdentifier = NULL;
|
||||
notificationData->soundType = kSoundTypeDefault;
|
||||
notificationData->soundVolume = -1.0f;
|
||||
notificationData->soundName = NULL;
|
||||
notificationData->interruptionLevel = kInterruptionLevelActive;
|
||||
notificationData->relevanceScore = 0;
|
||||
notificationData->triggerType = PUSH_TRIGGER;
|
||||
notificationData->userInfo = NULL;
|
||||
}
|
||||
|
||||
static enum UnityNotificationInterruptionLevel InterruptionLevelToUnity(UNNotificationInterruptionLevel level)
|
||||
API_AVAILABLE(ios(15.0))
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case UNNotificationInterruptionLevelActive:
|
||||
default:
|
||||
return kInterruptionLevelActive;
|
||||
case UNNotificationInterruptionLevelCritical:
|
||||
return kInterruptionLevelCritical;
|
||||
case UNNotificationInterruptionLevelPassive:
|
||||
return kInterruptionLevelPassive;
|
||||
case UNNotificationInterruptionLevelTimeSensitive:
|
||||
return kInterruptionLevelTimeSensitive;
|
||||
}
|
||||
}
|
||||
|
||||
static void parseCustomizedData(iOSNotificationData* notificationData, UNNotificationRequest* request)
|
||||
{
|
||||
NSDictionary* userInfo = request.content.userInfo;
|
||||
NSObject* customizedData = [userInfo objectForKey: @"data"];
|
||||
|
||||
// For local notifications, the customzied data is always a string.
|
||||
if (notificationData->triggerType == TIME_TRIGGER || notificationData->triggerType == CALENDAR_TRIGGER || customizedData == nil)
|
||||
{
|
||||
notificationData->userInfo = (__bridge_retained void*)userInfo;
|
||||
return;
|
||||
}
|
||||
|
||||
// For push notifications, we have to handle more cases.
|
||||
NSString* strData = ParseNotificationDataObject(customizedData);
|
||||
if (strData == nil)
|
||||
NSLog(@"Failed parsing notification userInfo[\"data\"]");
|
||||
|
||||
NSMutableDictionary* parsedUserInfo = [NSMutableDictionary dictionaryWithDictionary: userInfo];
|
||||
[parsedUserInfo setValue: strData forKey: @"data"];
|
||||
notificationData->userInfo = (__bridge_retained void*)parsedUserInfo;
|
||||
}
|
||||
|
||||
iOSNotificationData UNNotificationRequestToiOSNotificationData(UNNotificationRequest* request)
|
||||
{
|
||||
iOSNotificationData notificationData;
|
||||
initiOSNotificationData(¬ificationData);
|
||||
|
||||
UNNotificationContent* content = request.content;
|
||||
|
||||
notificationData.identifier = strdup([request.identifier UTF8String]);
|
||||
|
||||
if (content.title != nil && content.title.length > 0)
|
||||
notificationData.title = strdup([content.title UTF8String]);
|
||||
|
||||
if (content.body != nil && content.body.length > 0)
|
||||
notificationData.body = strdup([content.body UTF8String]);
|
||||
|
||||
notificationData.badge = [content.badge intValue];
|
||||
|
||||
if (content.subtitle != nil && content.subtitle.length > 0)
|
||||
notificationData.subtitle = strdup([content.subtitle UTF8String]);
|
||||
|
||||
if (content.categoryIdentifier != nil && content.categoryIdentifier.length > 0)
|
||||
notificationData.categoryIdentifier = strdup([content.categoryIdentifier UTF8String]);
|
||||
|
||||
if (content.threadIdentifier != nil && content.threadIdentifier.length > 0)
|
||||
notificationData.threadIdentifier = strdup([content.threadIdentifier UTF8String]);
|
||||
|
||||
if (@available(iOS 15.0, *))
|
||||
{
|
||||
notificationData.interruptionLevel = InterruptionLevelToUnity(content.interruptionLevel);
|
||||
notificationData.relevanceScore = content.relevanceScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
notificationData.interruptionLevel = kInterruptionLevelActive;
|
||||
notificationData.relevanceScore = 0;
|
||||
}
|
||||
|
||||
if ([request.trigger isKindOfClass: [UNTimeIntervalNotificationTrigger class]])
|
||||
{
|
||||
notificationData.triggerType = TIME_TRIGGER;
|
||||
|
||||
UNTimeIntervalNotificationTrigger* timeTrigger = (UNTimeIntervalNotificationTrigger*)request.trigger;
|
||||
notificationData.trigger.timeInterval.interval = timeTrigger.timeInterval;
|
||||
notificationData.trigger.timeInterval.repeats = timeTrigger.repeats;
|
||||
}
|
||||
else if ([request.trigger isKindOfClass: [UNCalendarNotificationTrigger class]])
|
||||
{
|
||||
notificationData.triggerType = CALENDAR_TRIGGER;
|
||||
|
||||
UNCalendarNotificationTrigger* calendarTrigger = (UNCalendarNotificationTrigger*)request.trigger;
|
||||
NSDateComponents* date = calendarTrigger.dateComponents;
|
||||
|
||||
notificationData.trigger.calendar.year = (int)date.year;
|
||||
notificationData.trigger.calendar.month = (int)date.month;
|
||||
notificationData.trigger.calendar.day = (int)date.day;
|
||||
notificationData.trigger.calendar.hour = (int)date.hour;
|
||||
notificationData.trigger.calendar.minute = (int)date.minute;
|
||||
notificationData.trigger.calendar.second = (int)date.second;
|
||||
notificationData.trigger.calendar.repeats = (int)calendarTrigger.repeats;
|
||||
}
|
||||
else if ([request.trigger isKindOfClass: [UNLocationNotificationTrigger class]])
|
||||
{
|
||||
#if UNITY_USES_LOCATION
|
||||
notificationData.triggerType = LOCATION_TRIGGER;
|
||||
|
||||
UNLocationNotificationTrigger* locationTrigger = (UNLocationNotificationTrigger*)request.trigger;
|
||||
CLCircularRegion *region = (CLCircularRegion*)locationTrigger.region;
|
||||
|
||||
notificationData.trigger.location.latitude = region.center.latitude;
|
||||
notificationData.trigger.location.longitude = region.center.longitude;
|
||||
notificationData.trigger.location.radius = region.radius;
|
||||
notificationData.trigger.location.notifyOnExit = region.notifyOnEntry;
|
||||
notificationData.trigger.location.notifyOnEntry = region.notifyOnExit;
|
||||
notificationData.trigger.location.repeats = locationTrigger.repeats;
|
||||
#endif
|
||||
}
|
||||
else if ([request.trigger isKindOfClass: [UNPushNotificationTrigger class]])
|
||||
{
|
||||
notificationData.triggerType = PUSH_TRIGGER;
|
||||
}
|
||||
else
|
||||
notificationData.triggerType = UNKNOWN_TRIGGER;
|
||||
|
||||
parseCustomizedData(¬ificationData, request);
|
||||
notificationData.attachments = (__bridge_retained void*)request.content.attachments;
|
||||
|
||||
return notificationData;
|
||||
}
|
||||
|
||||
void freeiOSNotificationData(iOSNotificationData* notificationData)
|
||||
{
|
||||
if (notificationData->identifier != NULL)
|
||||
free(notificationData->identifier);
|
||||
|
||||
if (notificationData->title != NULL)
|
||||
free(notificationData->title);
|
||||
|
||||
if (notificationData->body != NULL)
|
||||
free(notificationData->body);
|
||||
|
||||
if (notificationData->subtitle != NULL)
|
||||
free(notificationData->subtitle);
|
||||
|
||||
if (notificationData->categoryIdentifier != NULL)
|
||||
free(notificationData->categoryIdentifier);
|
||||
|
||||
if (notificationData->threadIdentifier != NULL)
|
||||
free(notificationData->threadIdentifier);
|
||||
|
||||
if (notificationData->soundName != NULL)
|
||||
free(notificationData->soundName);
|
||||
|
||||
if (notificationData->userInfo != NULL)
|
||||
{
|
||||
NSDictionary* userInfo = (__bridge_transfer NSDictionary*)notificationData->userInfo;
|
||||
userInfo = nil;
|
||||
}
|
||||
|
||||
if (notificationData->attachments != NULL)
|
||||
{
|
||||
NSArray* attachments = (__bridge_transfer NSArray*)notificationData->attachments;
|
||||
attachments = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void* _AddItemToNSDictionary(void* dict, const char* key, const char* value)
|
||||
{
|
||||
NSDictionary* dictionary;
|
||||
if (dict != NULL)
|
||||
dictionary = (__bridge NSDictionary*)dict;
|
||||
else
|
||||
{
|
||||
dictionary = [[NSMutableDictionary alloc] init];
|
||||
dict = (__bridge_retained void*)dictionary;
|
||||
}
|
||||
|
||||
NSString* k = [NSString stringWithUTF8String: key];
|
||||
NSString* v = value ? [NSString stringWithUTF8String: value] : @"";
|
||||
[dictionary setValue: v forKey: k];
|
||||
return dict;
|
||||
}
|
||||
|
||||
void* _AddAttachmentToNSArray(void* arr, const char* attId, const char* url, void** outError)
|
||||
{
|
||||
*outError = NULL;
|
||||
NSString* attachmentId = nil;
|
||||
if (attId != NULL)
|
||||
attachmentId = [NSString stringWithUTF8String: attId];
|
||||
NSURL* uri = [NSURL URLWithString: [NSString stringWithUTF8String: url]];
|
||||
NSError* error = nil;
|
||||
UNNotificationAttachment* attachment = [UNNotificationAttachment attachmentWithIdentifier: attachmentId URL: uri options: nil error: &error];
|
||||
if (attachment != nil)
|
||||
{
|
||||
NSMutableArray* array;
|
||||
if (arr != NULL)
|
||||
array = (__bridge NSMutableArray*)arr;
|
||||
else
|
||||
{
|
||||
array = [[NSMutableArray alloc] init];
|
||||
arr = (__bridge_retained void*)array;
|
||||
}
|
||||
|
||||
[array addObject: attachment];
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (error != nil)
|
||||
*outError = (__bridge_retained void*)error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _ReadNSDictionary(void* csDict, void* nsDict, void (*callback)(void* csDcit, const char*, const char*))
|
||||
{
|
||||
NSDictionary* dict = (__bridge NSDictionary*)nsDict;
|
||||
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
NSString* k = key;
|
||||
NSString* v = ParseNotificationDataObject(obj);
|
||||
if (v != nil)
|
||||
callback(csDict, k.UTF8String, v.UTF8String);
|
||||
else
|
||||
NSLog(@"Failed to parse value for key '%@'", key);
|
||||
}];
|
||||
}
|
||||
|
||||
void _ReadAttachmentsNSArray(void* csList, void* nsArray, void (*callback)(void*, const char*, const char*))
|
||||
{
|
||||
NSArray<UNNotificationAttachment*>* attachments = (__bridge NSArray<UNNotificationAttachment*>*)nsArray;
|
||||
[attachments enumerateObjectsUsingBlock:^(UNNotificationAttachment * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSString* idr = obj.identifier;
|
||||
NSString* url = obj.URL.absoluteString;
|
||||
callback(csList, idr.UTF8String, url.UTF8String);
|
||||
}];
|
||||
}
|
||||
|
||||
#endif
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a44b320ceb3374077b14c5d3a8f0037f
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
tvOS: tvOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// UnityNotificationManager.h
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import "UnityNotificationData.h"
|
||||
|
||||
@interface UnityNotificationManager : NSObject<UNUserNotificationCenterDelegate>
|
||||
|
||||
@property UNNotificationSettings* cachedNotificationSettings;
|
||||
|
||||
@property NotificationDataReceivedResponse onNotificationReceivedCallback;
|
||||
@property NotificationDataReceivedResponse onRemoteNotificationReceivedCallback;
|
||||
@property AuthorizationRequestResponse onAuthorizationCompletionCallback;
|
||||
|
||||
@property NSArray<UNNotificationRequest *> * cachedPendingNotificationRequests;
|
||||
@property NSArray<UNNotification *> * cachedDeliveredNotifications;
|
||||
|
||||
@property (nonatomic) UNNotification* lastReceivedNotification;
|
||||
@property NSString* lastRespondedNotificationAction;
|
||||
@property NSString* lastRespondedNotificationUserText;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (id)init;
|
||||
- (void)finishAuthorization:(struct iOSNotificationAuthorizationData*)authData forRequest:(void*)request;
|
||||
- (void)finishRemoteNotificationRegistration:(UNAuthorizationStatus)status notification:(NSNotification*)notification;
|
||||
- (void)updateScheduledNotificationList;
|
||||
- (void)updateDeliveredNotificationList;
|
||||
- (void)updateNotificationSettings;
|
||||
- (void)requestAuthorization:(NSInteger)authorizationOptions withRegisterRemote:(BOOL)registerRemote forRequest:(void*)request;
|
||||
- (void)unregisterForRemoteNotifications;
|
||||
- (void)scheduleLocalNotification:(iOSNotificationData*)data;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cef86cd5b8e5d4dc6acd332b2199c6c8
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude iOS: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
//
|
||||
// UnityNotificationManager.m
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
#import "UnityNotificationManager.h"
|
||||
|
||||
#if UNITY_USES_LOCATION
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#endif
|
||||
|
||||
@implementation UnityNotificationManager
|
||||
{
|
||||
NSLock* _lock;
|
||||
UNAuthorizationStatus _remoteNotificationsRegistered;
|
||||
NSInteger _remoteNotificationForegroundPresentationOptions;
|
||||
NSString* _deviceToken;
|
||||
NSPointerArray* _pendingRemoteAuthRequests;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static UnityNotificationManager *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[UnityNotificationManager alloc] init];
|
||||
});
|
||||
|
||||
[sharedInstance updateNotificationSettings];
|
||||
[sharedInstance updateScheduledNotificationList];
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
_lock = [[NSLock alloc] init];
|
||||
_remoteNotificationsRegistered = UNAuthorizationStatusNotDetermined;
|
||||
_deviceToken = nil;
|
||||
_pendingRemoteAuthRequests = nil;
|
||||
_remoteNotificationForegroundPresentationOptions = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityRemoteNotificationForegroundPresentationOptions"] integerValue];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)finishAuthorization:(struct iOSNotificationAuthorizationData*)authData forRequest:(void*)request
|
||||
{
|
||||
if (self.onAuthorizationCompletionCallback != NULL && request)
|
||||
self.onAuthorizationCompletionCallback(request, *authData);
|
||||
}
|
||||
|
||||
- (void)finishRemoteNotificationRegistration:(UNAuthorizationStatus)status notification:(NSNotification*)notification
|
||||
{
|
||||
struct iOSNotificationAuthorizationData authData;
|
||||
authData.granted = status == UNAuthorizationStatusAuthorized;
|
||||
authData.error = NULL;
|
||||
authData.deviceToken = NULL;
|
||||
NSString* deviceToken = nil;
|
||||
if (authData.granted)
|
||||
{
|
||||
deviceToken = [UnityNotificationManager deviceTokenFromNotification: notification];
|
||||
authData.deviceToken = [deviceToken UTF8String];
|
||||
}
|
||||
|
||||
[_lock lock];
|
||||
_remoteNotificationsRegistered = status;
|
||||
_deviceToken = deviceToken;
|
||||
NSPointerArray* pointers = _pendingRemoteAuthRequests;
|
||||
_pendingRemoteAuthRequests = nil;
|
||||
[_lock unlock];
|
||||
|
||||
while (pointers.count > 0)
|
||||
{
|
||||
unsigned long idx = pointers.count - 1;
|
||||
void* request = [pointers pointerAtIndex: idx];
|
||||
[pointers removePointerAtIndex: idx];
|
||||
[self finishAuthorization: &authData forRequest: request];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)requestAuthorization:(NSInteger)authorizationOptions withRegisterRemote:(BOOL)registerRemote forRequest:(void*)request
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
BOOL supportsPushNotification = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UnityAddRemoteNotificationCapability"] boolValue];
|
||||
registerRemote = registerRemote && supportsPushNotification;
|
||||
|
||||
[center requestAuthorizationWithOptions: authorizationOptions completionHandler:^(BOOL granted, NSError * _Nullable error)
|
||||
{
|
||||
BOOL authorizationRequestFinished = YES;
|
||||
struct iOSNotificationAuthorizationData authData;
|
||||
authData.granted = granted;
|
||||
authData.error = [[error localizedDescription]cStringUsingEncoding: NSUTF8StringEncoding];
|
||||
authData.deviceToken = "";
|
||||
|
||||
if (granted)
|
||||
{
|
||||
[_lock lock];
|
||||
if (registerRemote && _remoteNotificationsRegistered == UNAuthorizationStatusNotDetermined)
|
||||
{
|
||||
authorizationRequestFinished = NO;
|
||||
if (request)
|
||||
{
|
||||
if (_pendingRemoteAuthRequests == nil)
|
||||
_pendingRemoteAuthRequests = [NSPointerArray pointerArrayWithOptions: NSPointerFunctionsOpaqueMemory];
|
||||
[_pendingRemoteAuthRequests addPointer: request];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
});
|
||||
}
|
||||
else
|
||||
authData.deviceToken = [_deviceToken UTF8String];
|
||||
[_lock unlock];
|
||||
}
|
||||
else
|
||||
NSLog(@"Requesting notification authorization failed with: %@", error);
|
||||
|
||||
if (authorizationRequestFinished)
|
||||
[self finishAuthorization: &authData forRequest: request];
|
||||
[self updateNotificationSettings];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)unregisterForRemoteNotifications
|
||||
{
|
||||
[[UIApplication sharedApplication] unregisterForRemoteNotifications];
|
||||
_remoteNotificationsRegistered = UNAuthorizationStatusNotDetermined;
|
||||
}
|
||||
|
||||
+ (NSString*)deviceTokenFromNotification:(NSNotification*)notification
|
||||
{
|
||||
NSData* deviceTokenData;
|
||||
if ([notification.userInfo isKindOfClass: [NSData class]])
|
||||
deviceTokenData = (NSData*)notification.userInfo;
|
||||
else
|
||||
return nil;
|
||||
|
||||
NSUInteger len = deviceTokenData.length;
|
||||
if (len == 0)
|
||||
return nil;
|
||||
|
||||
const unsigned char *buffer = deviceTokenData.bytes;
|
||||
NSMutableString *str = [NSMutableString stringWithCapacity: (len * 2)];
|
||||
for (int i = 0; i < len; ++i)
|
||||
[str appendFormat: @"%02x", buffer[i]];
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Called when a notification is delivered to a foreground app.
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
|
||||
{
|
||||
iOSNotificationData notificationData;
|
||||
BOOL haveNotificationData = NO;
|
||||
if (self.onNotificationReceivedCallback != NULL)
|
||||
{
|
||||
notificationData = UNNotificationRequestToiOSNotificationData(notification.request);
|
||||
haveNotificationData = YES;
|
||||
self.onNotificationReceivedCallback(notificationData);
|
||||
}
|
||||
|
||||
BOOL showInForeground;
|
||||
NSInteger presentationOptions;
|
||||
|
||||
showInForeground = [[notification.request.content.userInfo objectForKey: @"showInForeground"] boolValue];
|
||||
if ([notification.request.trigger isKindOfClass: [UNPushNotificationTrigger class]])
|
||||
{
|
||||
presentationOptions = _remoteNotificationForegroundPresentationOptions;
|
||||
if (self.onRemoteNotificationReceivedCallback != NULL)
|
||||
{
|
||||
if (!haveNotificationData)
|
||||
{
|
||||
notificationData = UNNotificationRequestToiOSNotificationData(notification.request);
|
||||
haveNotificationData = YES;
|
||||
}
|
||||
|
||||
self.onRemoteNotificationReceivedCallback(notificationData);
|
||||
}
|
||||
else
|
||||
{
|
||||
showInForeground = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
presentationOptions = [[notification.request.content.userInfo objectForKey: @"showInForegroundPresentationOptions"] intValue];
|
||||
}
|
||||
|
||||
if (haveNotificationData)
|
||||
freeiOSNotificationData(¬ificationData);
|
||||
|
||||
if (showInForeground)
|
||||
completionHandler(presentationOptions);
|
||||
else
|
||||
completionHandler(UNNotificationPresentationOptionNone);
|
||||
|
||||
[[UnityNotificationManager sharedInstance] updateDeliveredNotificationList];
|
||||
}
|
||||
|
||||
// Called to let your app know which action was selected by the user for a given notification.
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(nonnull void(^)(void))completionHandler
|
||||
{
|
||||
self.lastReceivedNotification = response.notification;
|
||||
self.lastRespondedNotificationAction = response.actionIdentifier;
|
||||
if ([response isKindOfClass: UNTextInputNotificationResponse.class])
|
||||
{
|
||||
UNTextInputNotificationResponse* resp = (UNTextInputNotificationResponse*)response;
|
||||
self.lastRespondedNotificationUserText = resp.userText;
|
||||
}
|
||||
else
|
||||
self.lastRespondedNotificationUserText = nil;
|
||||
completionHandler();
|
||||
[[UnityNotificationManager sharedInstance] updateDeliveredNotificationList];
|
||||
}
|
||||
|
||||
- (void)updateScheduledNotificationList
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
|
||||
self.cachedPendingNotificationRequests = requests;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateDeliveredNotificationList
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
|
||||
self.cachedDeliveredNotifications = notifications;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateNotificationSettings
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
|
||||
self.cachedNotificationSettings = settings;
|
||||
}];
|
||||
}
|
||||
|
||||
bool validateAuthorizationStatus(UnityNotificationManager* manager)
|
||||
{
|
||||
UNAuthorizationStatus authorizationStatus = manager.cachedNotificationSettings.authorizationStatus;
|
||||
|
||||
if (authorizationStatus == UNAuthorizationStatusAuthorized)
|
||||
return true;
|
||||
|
||||
if (authorizationStatus == UNAuthorizationStatusProvisional)
|
||||
return true;
|
||||
|
||||
NSLog(@"Attempting to schedule a local notification without authorization, please call RequestAuthorization first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)scheduleLocalNotification:(iOSNotificationData*)data
|
||||
{
|
||||
if (!validateAuthorizationStatus(self))
|
||||
return;
|
||||
|
||||
assert(self.onNotificationReceivedCallback != NULL);
|
||||
|
||||
NSDictionary* userInfo = (__bridge_transfer NSDictionary*)data->userInfo;
|
||||
data->userInfo = NULL;
|
||||
|
||||
// Convert from iOSNotificationData to UNMutableNotificationContent.
|
||||
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
||||
|
||||
NSString* dataTitle = data->title ? [NSString stringWithUTF8String: data->title] : [NSString string];
|
||||
NSString* dataBody = data->body ? [NSString stringWithUTF8String: data->body] : [NSString string];
|
||||
|
||||
content.title = [NSString localizedUserNotificationStringForKey: dataTitle arguments: nil];
|
||||
content.body = [NSString localizedUserNotificationStringForKey: dataBody arguments: nil];
|
||||
content.userInfo = userInfo;
|
||||
|
||||
if (data->badge >= 0)
|
||||
content.badge = [NSNumber numberWithInt: data->badge];
|
||||
|
||||
if (data->subtitle != NULL)
|
||||
content.subtitle = [NSString localizedUserNotificationStringForKey: [NSString stringWithUTF8String: data->subtitle] arguments: nil];
|
||||
|
||||
if (data->categoryIdentifier != NULL)
|
||||
content.categoryIdentifier = [NSString stringWithUTF8String: data->categoryIdentifier];
|
||||
|
||||
if (data->threadIdentifier != NULL)
|
||||
content.threadIdentifier = [NSString stringWithUTF8String: data->threadIdentifier];
|
||||
|
||||
UNNotificationSound* sound = [self soundForNotification: data];
|
||||
if (sound != nil)
|
||||
content.sound = sound;
|
||||
if (@available(iOS 15.0, *))
|
||||
{
|
||||
content.interruptionLevel = [self unityInterruptionLevelToIos: data->interruptionLevel];
|
||||
content.relevanceScore = data->relevanceScore;
|
||||
}
|
||||
|
||||
content.attachments = (__bridge_transfer NSArray<UNNotificationAttachment*>*)data->attachments;
|
||||
data->attachments = NULL;
|
||||
|
||||
NSString* identifier = [NSString stringWithUTF8String: data->identifier];
|
||||
// Generate UNNotificationTrigger from iOSNotificationData.
|
||||
UNNotificationTrigger* trigger;
|
||||
if (data->triggerType == TIME_TRIGGER)
|
||||
{
|
||||
trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval: data->trigger.timeInterval.interval repeats: data->trigger.timeInterval.repeats];
|
||||
}
|
||||
else if (data->triggerType == CALENDAR_TRIGGER)
|
||||
{
|
||||
NSDateComponents* date = [[NSDateComponents alloc] init];
|
||||
if (data->trigger.calendar.year >= 0)
|
||||
date.year = data->trigger.calendar.year;
|
||||
if (data->trigger.calendar.month >= 0)
|
||||
date.month = data->trigger.calendar.month;
|
||||
if (data->trigger.calendar.day >= 0)
|
||||
date.day = data->trigger.calendar.day;
|
||||
if (data->trigger.calendar.hour >= 0)
|
||||
date.hour = data->trigger.calendar.hour;
|
||||
if (data->trigger.calendar.minute >= 0)
|
||||
date.minute = data->trigger.calendar.minute;
|
||||
if (data->trigger.calendar.second >= 0)
|
||||
date.second = data->trigger.calendar.second;
|
||||
|
||||
date.calendar = [NSCalendar calendarWithIdentifier: NSCalendarIdentifierGregorian];
|
||||
if ([@"1" isEqualToString: [userInfo objectForKey: @"OriginalUtc"]])
|
||||
date.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
|
||||
|
||||
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents: date repeats: data->trigger.calendar.repeats];
|
||||
NSLog(@"Notification will show after %f s.", ((UNCalendarNotificationTrigger*)trigger).nextTriggerDate.timeIntervalSinceNow);
|
||||
}
|
||||
else if (data->triggerType == LOCATION_TRIGGER)
|
||||
{
|
||||
#if UNITY_USES_LOCATION
|
||||
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(data->trigger.location.latitude, data->trigger.location.longitude);
|
||||
|
||||
CLCircularRegion* region = [[CLCircularRegion alloc] initWithCenter: center
|
||||
radius: data->trigger.location.radius identifier: identifier];
|
||||
region.notifyOnEntry = data->trigger.location.notifyOnEntry;
|
||||
region.notifyOnExit = data->trigger.location.notifyOnExit;
|
||||
|
||||
trigger = [UNLocationNotificationTrigger triggerWithRegion: region repeats: data->trigger.location.repeats];
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier: identifier content: content trigger: trigger];
|
||||
|
||||
// Schedule the notification.
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center addNotificationRequest: request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (error != NULL)
|
||||
NSLog(@"%@", [error localizedDescription]);
|
||||
|
||||
[self updateScheduledNotificationList];
|
||||
}];
|
||||
}
|
||||
|
||||
- (UNNotificationSound*)soundForNotification:(const iOSNotificationData*)data
|
||||
{
|
||||
NSString* soundName = nil;
|
||||
if (data->soundName != NULL)
|
||||
soundName = [NSString stringWithUTF8String: data->soundName];
|
||||
|
||||
switch (data->soundType)
|
||||
{
|
||||
case kSoundTypeNone:
|
||||
return nil;
|
||||
case kSoundTypeCritical:
|
||||
if (soundName != nil)
|
||||
{
|
||||
if (data->soundVolume < 0)
|
||||
return [UNNotificationSound criticalSoundNamed: soundName];
|
||||
return [UNNotificationSound criticalSoundNamed: soundName withAudioVolume: data->soundVolume];
|
||||
}
|
||||
if (data->soundVolume >= 0)
|
||||
return [UNNotificationSound defaultCriticalSoundWithAudioVolume: data->soundVolume];
|
||||
return UNNotificationSound.defaultCriticalSound;
|
||||
case kSoundTypeRingtone:
|
||||
if (@available(iOS 15.2, *))
|
||||
{
|
||||
if (soundName != nil)
|
||||
return [UNNotificationSound ringtoneSoundNamed: soundName];
|
||||
return UNNotificationSound.defaultRingtoneSound;
|
||||
}
|
||||
// continue to default
|
||||
case kSoundTypeDefault:
|
||||
default:
|
||||
if (soundName != nil)
|
||||
return [UNNotificationSound soundNamed: soundName];
|
||||
return UNNotificationSound.defaultSound;
|
||||
}
|
||||
}
|
||||
|
||||
- (UNNotificationInterruptionLevel)unityInterruptionLevelToIos:(int)level
|
||||
API_AVAILABLE(ios(15.0))
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case kInterruptionLevelActive:
|
||||
return UNNotificationInterruptionLevelActive;
|
||||
case kInterruptionLevelCritical:
|
||||
return UNNotificationInterruptionLevelCritical;
|
||||
case kInterruptionLevelPassive:
|
||||
return UNNotificationInterruptionLevelPassive;
|
||||
case kInterruptionLevelTimeSensitive:
|
||||
return UNNotificationInterruptionLevelTimeSensitive;
|
||||
default:
|
||||
return UNNotificationInterruptionLevelActive;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d15de7aefd6e94456967d000884a11ea
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
tvOS: tvOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
//
|
||||
// UnityNotificationWrapper.m
|
||||
// iOS.notifications
|
||||
//
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "UnityNotificationManager.h"
|
||||
|
||||
|
||||
int _NativeSizeof_iOSNotificationAuthorizationData()
|
||||
{
|
||||
return sizeof(iOSNotificationAuthorizationData);
|
||||
}
|
||||
|
||||
int _NativeSizeof_iOSNotificationData()
|
||||
{
|
||||
return sizeof(iOSNotificationData);
|
||||
}
|
||||
|
||||
int _NativeSizeof_NotificationSettingsData()
|
||||
{
|
||||
return sizeof(NotificationSettingsData);
|
||||
}
|
||||
|
||||
void _FreeUnmanagediOSNotificationDataArray(iOSNotificationData* ptr, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
freeiOSNotificationData(&ptr[i]);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void _SetAuthorizationRequestReceivedDelegate(AuthorizationRequestResponse callback)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
manager.onAuthorizationCompletionCallback = callback;
|
||||
}
|
||||
|
||||
void _SetNotificationReceivedDelegate(NotificationDataReceivedResponse callback)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
manager.onNotificationReceivedCallback = callback;
|
||||
}
|
||||
|
||||
void _SetRemoteNotificationReceivedDelegate(NotificationDataReceivedResponse callback)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
manager.onRemoteNotificationReceivedCallback = callback;
|
||||
}
|
||||
|
||||
void _RequestAuthorization(void* request, int options, BOOL registerRemote)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager requestAuthorization: options withRegisterRemote: registerRemote forRequest: request];
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
center.delegate = manager;
|
||||
}
|
||||
|
||||
int _RegisteredForRemoteNotifications()
|
||||
{
|
||||
return [UIApplication sharedApplication].registeredForRemoteNotifications;
|
||||
}
|
||||
|
||||
void _UnregisterForRemoteNotifications()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager unregisterForRemoteNotifications];
|
||||
}
|
||||
|
||||
void _ScheduleLocalNotification(iOSNotificationData data)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
[manager scheduleLocalNotification: &data];
|
||||
}
|
||||
|
||||
NotificationSettingsData _GetNotificationSettings()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
return UNNotificationSettingsToNotificationSettingsData(manager.cachedNotificationSettings);
|
||||
}
|
||||
|
||||
iOSNotificationData* _GetScheduledNotificationDataArray(int* count)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
NSArray<UNNotificationRequest*>* pendingNotificationRequests = manager.cachedPendingNotificationRequests;
|
||||
if (pendingNotificationRequests == nil)
|
||||
{
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
*count = (int)pendingNotificationRequests.count;
|
||||
if (*count == 0)
|
||||
return NULL;
|
||||
|
||||
iOSNotificationData* ret = (iOSNotificationData*)malloc(*count * sizeof(iOSNotificationData));
|
||||
for (int i = 0; i < *count; ++i)
|
||||
{
|
||||
UNNotificationRequest *request = pendingNotificationRequests[i];
|
||||
ret[i] = UNNotificationRequestToiOSNotificationData(request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
iOSNotificationData* _GetDeliveredNotificationDataArray(int* count)
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
NSArray<UNNotification*>* deliveredNotifications = manager.cachedDeliveredNotifications;
|
||||
if (deliveredNotifications == nil)
|
||||
{
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
*count = (int)deliveredNotifications.count;
|
||||
if (*count == 0)
|
||||
return NULL;
|
||||
|
||||
iOSNotificationData* ret = (iOSNotificationData*)malloc(*count * sizeof(iOSNotificationData));
|
||||
for (int i = 0; i < *count; ++i)
|
||||
{
|
||||
UNNotification* notification = deliveredNotifications[i];
|
||||
ret[i] = UNNotificationRequestToiOSNotificationData(notification.request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _RemoveScheduledNotification(const char* identifier)
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center removePendingNotificationRequestsWithIdentifiers: @[[NSString stringWithUTF8String: identifier]]];
|
||||
[[UnityNotificationManager sharedInstance] updateScheduledNotificationList];
|
||||
}
|
||||
|
||||
void _RemoveAllScheduledNotifications()
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center removeAllPendingNotificationRequests];
|
||||
[[UnityNotificationManager sharedInstance] updateScheduledNotificationList];
|
||||
}
|
||||
|
||||
void _RemoveDeliveredNotification(const char* identifier)
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center removeDeliveredNotificationsWithIdentifiers: @[[NSString stringWithUTF8String: identifier]]];
|
||||
[[UnityNotificationManager sharedInstance] updateDeliveredNotificationList];
|
||||
}
|
||||
|
||||
void _RemoveAllDeliveredNotifications()
|
||||
{
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center removeAllDeliveredNotifications];
|
||||
[[UnityNotificationManager sharedInstance] updateDeliveredNotificationList];
|
||||
}
|
||||
|
||||
void _SetApplicationBadge(long badge)
|
||||
{
|
||||
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: badge];
|
||||
}
|
||||
|
||||
long _GetApplicationBadge()
|
||||
{
|
||||
return [UIApplication sharedApplication].applicationIconBadgeNumber;
|
||||
}
|
||||
|
||||
bool _GetAppOpenedUsingNotification()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
return manager.lastReceivedNotification != NULL;
|
||||
}
|
||||
|
||||
iOSNotificationData* _GetLastNotificationData()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
UNNotification* notification = manager.lastReceivedNotification;
|
||||
if (notification == nil)
|
||||
return NULL;
|
||||
UNNotificationRequest* request = notification.request;
|
||||
if (request == nil)
|
||||
return NULL;
|
||||
iOSNotificationData* ret = (iOSNotificationData*)malloc(sizeof(iOSNotificationData));
|
||||
*ret = UNNotificationRequestToiOSNotificationData(request);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* _GetLastRespondedNotificationAction()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
NSString* action = manager.lastRespondedNotificationAction;
|
||||
if (action == nil)
|
||||
return NULL;
|
||||
return strdup(action.UTF8String);
|
||||
}
|
||||
|
||||
const char* _GetLastRespondedNotificationUserText()
|
||||
{
|
||||
UnityNotificationManager* manager = [UnityNotificationManager sharedInstance];
|
||||
NSString* userText = manager.lastRespondedNotificationUserText;
|
||||
if (userText == nil)
|
||||
return NULL;
|
||||
return strdup(userText.UTF8String);
|
||||
}
|
||||
|
||||
static NSObject* CreateNotificationActionIcon(int iconType, const char* icon)
|
||||
{
|
||||
enum IconType
|
||||
{
|
||||
kIconTypeNone = 0,
|
||||
kIconTypeSystemImageName = 1,
|
||||
kIconTypeTemplateImageName = 2,
|
||||
};
|
||||
|
||||
NSObject* actionIcon = nil;
|
||||
|
||||
if (@available(iOS 15.0, *))
|
||||
{
|
||||
if (icon != NULL && iconType != kIconTypeNone)
|
||||
{
|
||||
NSString* iconName = [NSString stringWithUTF8String: icon];
|
||||
switch (iconType)
|
||||
{
|
||||
case kIconTypeSystemImageName:
|
||||
actionIcon = [UNNotificationActionIcon iconWithSystemImageName: iconName];
|
||||
break;
|
||||
case kIconTypeTemplateImageName:
|
||||
actionIcon = [UNNotificationActionIcon iconWithTemplateImageName: iconName];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return actionIcon;
|
||||
}
|
||||
|
||||
void* _CreateUNNotificationAction(const char* identifier, const char* title, int options, int iconType, const char* icon)
|
||||
{
|
||||
UNNotificationActionOptions opts = (UNNotificationActionOptions)options;
|
||||
NSString* idr = [NSString stringWithUTF8String: identifier];
|
||||
NSString* titl = [NSString stringWithUTF8String: title];
|
||||
UNNotificationAction* action;
|
||||
|
||||
if (@available(iOS 15.0, *))
|
||||
{
|
||||
UNNotificationActionIcon* actionIcon = (UNNotificationActionIcon*)CreateNotificationActionIcon(iconType, icon);
|
||||
action = [UNNotificationAction actionWithIdentifier: idr title: titl options: opts icon: actionIcon];
|
||||
}
|
||||
else
|
||||
action = [UNNotificationAction actionWithIdentifier: idr title: titl options: opts];
|
||||
|
||||
return (__bridge_retained void*)action;
|
||||
}
|
||||
|
||||
void* _CreateUNTextInputNotificationAction(const char* identifier, const char* title, int options, int iconType, const char* icon, const char* buttonTitle, const char* placeholder)
|
||||
{
|
||||
UNNotificationActionOptions opts = (UNNotificationActionOptions)options;
|
||||
NSString* idr = [NSString stringWithUTF8String: identifier];
|
||||
NSString* titl = [NSString stringWithUTF8String: title];
|
||||
NSString* btnTitle = [NSString stringWithUTF8String: buttonTitle];
|
||||
NSString* placeHolder = placeholder ? [NSString stringWithUTF8String: placeholder] : NULL;
|
||||
UNTextInputNotificationAction* action;
|
||||
|
||||
if (@available(iOS 15.0, *))
|
||||
{
|
||||
UNNotificationActionIcon* actionIcon = (UNNotificationActionIcon*)CreateNotificationActionIcon(iconType, icon);
|
||||
action = [UNTextInputNotificationAction actionWithIdentifier: idr title: titl options: opts icon: actionIcon textInputButtonTitle: btnTitle textInputPlaceholder: placeHolder];
|
||||
}
|
||||
else
|
||||
action = [UNTextInputNotificationAction actionWithIdentifier: idr title: titl options: opts textInputButtonTitle: btnTitle textInputPlaceholder: placeHolder];
|
||||
|
||||
return (__bridge_retained void*)action;
|
||||
}
|
||||
|
||||
void _ReleaseNSObject(void* obj)
|
||||
{
|
||||
NSObject* a = (__bridge_transfer NSObject*)obj;
|
||||
a = nil;
|
||||
}
|
||||
|
||||
const char* _NSErrorToMessage(void* error)
|
||||
{
|
||||
NSError* e = (__bridge_transfer NSError*)error;
|
||||
NSString* msg = e.localizedDescription;
|
||||
return strdup(msg.UTF8String);
|
||||
}
|
||||
|
||||
void* _AddActionToNSArray(void* actions, void* action, int capacity)
|
||||
{
|
||||
NSMutableArray<UNNotificationAction*>* array;
|
||||
void* ret = actions;
|
||||
if (actions == NULL)
|
||||
{
|
||||
array = [NSMutableArray arrayWithCapacity: capacity];
|
||||
ret = (__bridge_retained void*)array;
|
||||
}
|
||||
else
|
||||
array = (__bridge NSMutableArray<UNNotificationAction*>*)actions;
|
||||
UNNotificationAction* a = (__bridge UNNotificationAction*)action;
|
||||
[array addObject: a];
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* _AddStringToNSArray(void* array, const char* str, int capacity)
|
||||
{
|
||||
NSMutableArray<NSString*>* arr;
|
||||
void* ret = array;
|
||||
if (array == NULL)
|
||||
{
|
||||
arr = [NSMutableArray arrayWithCapacity: capacity];
|
||||
ret = (__bridge_retained void*)arr;
|
||||
}
|
||||
else
|
||||
arr = (__bridge NSMutableArray<NSString*>*)array;
|
||||
NSString* s = [NSString stringWithUTF8String: str];
|
||||
[arr addObject: s];
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* _CreateUNNotificationCategory(const char* identifier, const char* hiddenPreviewsBodyPlaceholder, const char* summaryFormat,
|
||||
int options, void* actions, void* intentIdentifiers)
|
||||
{
|
||||
NSString* idr = [NSString stringWithUTF8String: identifier];
|
||||
NSString* placeholder = hiddenPreviewsBodyPlaceholder ? [NSString stringWithUTF8String: hiddenPreviewsBodyPlaceholder] : nil;
|
||||
NSString* summary = summaryFormat ? [NSString stringWithUTF8String: summaryFormat] : nil;
|
||||
NSArray<UNNotificationAction*>* acts = (__bridge_transfer NSArray<UNNotificationAction*>*)actions;
|
||||
NSArray<NSString*>* intents = (__bridge_transfer NSArray<NSString*>*)intentIdentifiers;
|
||||
UNNotificationCategoryOptions opts = (UNNotificationCategoryOptions)options;
|
||||
|
||||
UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier: idr actions: acts intentIdentifiers: intents hiddenPreviewsBodyPlaceholder: placeholder categorySummaryFormat: summary options: opts];
|
||||
return (__bridge_retained void*)category;
|
||||
}
|
||||
|
||||
void* _AddCategoryToCategorySet(void* categorySet, void* category)
|
||||
{
|
||||
UNNotificationCategory* cat = (__bridge_transfer UNNotificationCategory*)category;
|
||||
NSMutableSet<UNNotificationCategory*>* categories;
|
||||
if (categorySet == NULL)
|
||||
{
|
||||
categories = [NSMutableSet setWithObject: cat];
|
||||
return (__bridge_retained void*)categories;
|
||||
}
|
||||
|
||||
categories = (__bridge NSMutableSet<UNNotificationCategory*>*)categorySet;
|
||||
[categories addObject: cat];
|
||||
return categorySet;
|
||||
}
|
||||
|
||||
void _SetNotificationCategories(void* categorySet)
|
||||
{
|
||||
NSMutableSet<UNNotificationCategory*>* categories = (__bridge_transfer NSMutableSet<UNNotificationCategory*>*)categorySet;
|
||||
[UNUserNotificationCenter.currentNotificationCenter setNotificationCategories: categories];
|
||||
}
|
||||
|
||||
void _OpenNotificationSettings()
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString: UIApplicationOpenSettingsURLString];
|
||||
UIApplication* app = [UIApplication sharedApplication];
|
||||
if ([app canOpenURL: url])
|
||||
[app openURL: url options: @{} completionHandler: nil];
|
||||
}
|
||||
|
||||
#endif
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce0614a77cbc545b99eca271d5c36809
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
tvOS: tvOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Unity.Notifications.iOS",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor",
|
||||
"iOS"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e8e55397bd004beaba78a667566665f
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Vendored
+547
@@ -0,0 +1,547 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants indicating how to present a notification in a foreground app
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationpresentationoptions?language=objc"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PresentationOption
|
||||
{
|
||||
/// <summary>
|
||||
/// No options are set.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Apply the notification's badge value to the app’s icon.
|
||||
/// </summary>
|
||||
Badge = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Play the sound associated with the notification.
|
||||
/// </summary>
|
||||
Sound = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Display the alert using the content provided by the notification.
|
||||
/// </summary>
|
||||
Alert = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Show the notification in Notification Center.
|
||||
/// </summary>
|
||||
List = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Present the notification as a banner.
|
||||
/// </summary>
|
||||
Banner = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of sound to use for the notification.
|
||||
/// See Apple documentation for details.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationsound?language=objc"/>
|
||||
/// </summary>
|
||||
public enum NotificationSoundType
|
||||
{
|
||||
/// <summary>
|
||||
/// Play the default sound.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Critical sound (bypass Do Not Disturb)
|
||||
/// </summary>
|
||||
Critical = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Ringtone sound.
|
||||
/// </summary>
|
||||
Ringtone = 2,
|
||||
|
||||
/// <summary>
|
||||
/// No sound.
|
||||
/// </summary>
|
||||
None = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Importance and delivery timing of a notification.
|
||||
/// See Apple documentation for details. Available since iOS 15, always Active on lower versions.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel?language=objc"/>
|
||||
/// </summary>
|
||||
public enum NotificationInterruptionLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Default level. The system presents the notification immediately, lights up the screen, and can play a sound.
|
||||
/// </summary>
|
||||
Active = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The system presents the notification immediately, lights up the screen, and bypasses the mute switch to play a sound.
|
||||
/// </summary>
|
||||
Critical = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The system adds the notification to the notification list without lighting up the screen or playing a sound.
|
||||
/// </summary>
|
||||
Passive = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The system presents the notification immediately, lights up the screen, and can play a sound, but won’t break through system notification controls.
|
||||
/// </summary>
|
||||
TimeSensitive = 3,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TimeTriggerData
|
||||
{
|
||||
public Int32 interval;
|
||||
public Byte repeats;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct CalendarTriggerData
|
||||
{
|
||||
public Int32 year;
|
||||
public Int32 month;
|
||||
public Int32 day;
|
||||
public Int32 hour;
|
||||
public Int32 minute;
|
||||
public Int32 second;
|
||||
public Byte repeats;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct LocationTriggerData
|
||||
{
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public float radius;
|
||||
public Byte notifyOnEntry;
|
||||
public Byte notifyOnExit;
|
||||
public Byte repeats;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct TriggerData
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public TimeTriggerData timeInterval;
|
||||
[FieldOffset(0)]
|
||||
public CalendarTriggerData calendar;
|
||||
[FieldOffset(0)]
|
||||
public LocationTriggerData location;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct iOSNotificationData
|
||||
{
|
||||
public string identifier;
|
||||
public string title;
|
||||
public string body;
|
||||
public Int32 badge;
|
||||
public string subtitle;
|
||||
public string categoryIdentifier;
|
||||
public string threadIdentifier;
|
||||
public Int32 soundType;
|
||||
public float soundVolume;
|
||||
public string soundName;
|
||||
public Int32 interruptionLevel;
|
||||
public double relevanceScore;
|
||||
|
||||
public IntPtr userInfo;
|
||||
public IntPtr attachments;
|
||||
|
||||
// Trigger
|
||||
public Int32 triggerType;
|
||||
public TriggerData trigger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The iOSNotification class is used schedule local notifications. It includes the content of the notification and the trigger conditions for delivery.
|
||||
/// An instance of this class is also returned when receiving remote notifications..
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create an instance of this class when you want to schedule the delivery of a local notification. It contains the entire notification payload to be delivered
|
||||
/// (which corresponds to UNNotificationContent) and also the NotificationTrigger object with the conditions that trigger the delivery of the notification.
|
||||
/// To schedule the delivery of your notification, pass an instance of this class to the <see cref="iOSNotificationCenter.ScheduleNotification"/> method.
|
||||
/// </remarks>
|
||||
public class iOSNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique identifier for this notification request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If not explicitly specified the identifier will be automatically generated when creating the notification.
|
||||
/// </remarks>
|
||||
public string Identifier
|
||||
{
|
||||
get { return data.identifier; }
|
||||
set { data.identifier = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the app-defined category object.
|
||||
/// </summary>
|
||||
public string CategoryIdentifier
|
||||
{
|
||||
get { return data.categoryIdentifier; }
|
||||
set { data.categoryIdentifier = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An identifier that used to group related notifications together.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Automatic notification grouping according to the thread identifier is only supported on iOS 12 and above.
|
||||
/// </remarks>
|
||||
public string ThreadIdentifier
|
||||
{
|
||||
get { return data.threadIdentifier; }
|
||||
set { data.threadIdentifier = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A short description of the reason for the notification.
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get { return data.title; }
|
||||
set { data.title = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A secondary description of the reason for the notification.
|
||||
/// </summary>
|
||||
public string Subtitle
|
||||
{
|
||||
get { return data.subtitle; }
|
||||
set { data.subtitle = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The message displayed in the notification alert.
|
||||
/// See Apple's documentation for details.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcontent/1649863-body"/>
|
||||
/// </summary>
|
||||
public string Body
|
||||
{
|
||||
get { return data.body; }
|
||||
set { data.body = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the notification alert should be shown when the app is open.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Subscribe to the <see cref="iOSNotificationCenter.OnNotificationReceived"/> event to receive a callback when the notification is triggered.
|
||||
/// </remarks>
|
||||
public bool ShowInForeground
|
||||
{
|
||||
get
|
||||
{
|
||||
string value;
|
||||
if (userInfo.TryGetValue("showInForeground", out value))
|
||||
return value == "YES";
|
||||
return false;
|
||||
}
|
||||
set { userInfo["showInForeground"] = value ? "YES" : "NO"; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Presentation options for displaying the local of notification when the app is running. Only works if <see cref="iOSNotification.ShowInForeground"/> is enabled and user has allowed enabled the requested options for your app.
|
||||
/// </summary>
|
||||
public PresentationOption ForegroundPresentationOption
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
string value;
|
||||
if (userInfo.TryGetValue("showInForegroundPresentationOptions", out value))
|
||||
return (PresentationOption)Int32.Parse(value);
|
||||
return default;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
set { userInfo["showInForegroundPresentationOptions"] = ((int)value).ToString(); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The number to display as a badge on the app’s icon.
|
||||
/// </summary>
|
||||
public int Badge
|
||||
{
|
||||
get { return data.badge; }
|
||||
set { data.badge = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of sound to be played.
|
||||
/// </summary>
|
||||
public NotificationSoundType SoundType
|
||||
{
|
||||
get { return (NotificationSoundType)data.soundType; }
|
||||
set { data.soundType = (int)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the sound to be played. Use null for system default sound.
|
||||
/// See Apple documentation for named sounds and sound file placement.
|
||||
/// </summary>
|
||||
public string SoundName
|
||||
{
|
||||
get { return data.soundName; }
|
||||
set { data.soundName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The volume for the sound. Use null to use the default volume.
|
||||
/// See Apple documentation for supported values.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationsound/2963118-defaultcriticalsoundwithaudiovol?language=objc"/>
|
||||
/// </summary>
|
||||
public float? SoundVolume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The notification’s importance and required delivery timing.
|
||||
/// </summary>
|
||||
public NotificationInterruptionLevel InterruptionLevel
|
||||
{
|
||||
get { return (NotificationInterruptionLevel)data.interruptionLevel; }
|
||||
set { data.interruptionLevel = (int)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The score the system uses to determine if the notification is the summary’s featured notification.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcontent/3821031-relevancescore?language=objc"/>
|
||||
/// </summary>
|
||||
public double RelevanceScore
|
||||
{
|
||||
get { return data.relevanceScore; }
|
||||
set { data.relevanceScore = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arbitrary string data which can be retrieved when the notification is used to open the app or is received while the app is running.
|
||||
/// Push notification is sent to the device as <see href="https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification?language=objc">JSON</see>.
|
||||
/// The value for data key is set to the Data property on notification.
|
||||
/// </summary>
|
||||
public string Data
|
||||
{
|
||||
get
|
||||
{
|
||||
string value;
|
||||
userInfo.TryGetValue("data", out value);
|
||||
return value;
|
||||
}
|
||||
set { userInfo["data"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Key-value collection sent or received with the notification.
|
||||
/// Note, that some of the other notification properties are transfered using this collection, it is not recommended to modify existing items.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> UserInfo
|
||||
{
|
||||
get { return userInfo; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of notification attachments.
|
||||
/// Notification attachments can be images, audio or video files. Refer to Apple documentation on supported formats.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649857-attachments?language=objc"/>
|
||||
/// </summary>
|
||||
public List<iOSNotificationAttachment> Attachments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The conditions that trigger the delivery of the notification.
|
||||
/// For notification that were already delivered and whose instance was returned by <see cref="iOSNotificationCenter.OnRemoteNotificationReceived"/> or <see cref="iOSNotificationCenter.OnRemoteNotificationReceived"/>
|
||||
/// use this property to determine what caused the delivery to occur. You can do this by comparing <see cref="iOSNotification.Trigger"/> to any of the notification trigger types that implement it, such as
|
||||
/// <see cref="iOSNotificationLocationTrigger"/>, <see cref="iOSNotificationPushTrigger"/>, <see cref="iOSNotificationTimeIntervalTrigger"/>, <see cref="iOSNotificationCalendarTrigger"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// notification.Trigger is iOSNotificationPushTrigger
|
||||
/// </code>
|
||||
/// </example>
|
||||
public iOSNotificationTrigger Trigger
|
||||
{
|
||||
set
|
||||
{
|
||||
switch (value.Type)
|
||||
{
|
||||
case iOSNotificationTriggerType.TimeInterval:
|
||||
{
|
||||
var trigger = (iOSNotificationTimeIntervalTrigger)value;
|
||||
data.trigger.timeInterval.interval = trigger.timeInterval;
|
||||
|
||||
if (trigger.Repeats && trigger.timeInterval < 60)
|
||||
throw new ArgumentException("Time interval must be 60 seconds or greater for repeating notifications.");
|
||||
|
||||
data.trigger.timeInterval.repeats = (byte)(trigger.Repeats ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case iOSNotificationTriggerType.Calendar:
|
||||
{
|
||||
var trigger = ((iOSNotificationCalendarTrigger)value);
|
||||
if (userInfo == null)
|
||||
userInfo = new Dictionary<string, string>();
|
||||
userInfo["OriginalUtc"] = trigger.UtcTime ? "1" : "0";
|
||||
data.trigger.calendar.year = trigger.Year != null ? trigger.Year.Value : -1;
|
||||
data.trigger.calendar.month = trigger.Month != null ? trigger.Month.Value : -1;
|
||||
data.trigger.calendar.day = trigger.Day != null ? trigger.Day.Value : -1;
|
||||
data.trigger.calendar.hour = trigger.Hour != null ? trigger.Hour.Value : -1;
|
||||
data.trigger.calendar.minute = trigger.Minute != null ? trigger.Minute.Value : -1;
|
||||
data.trigger.calendar.second = trigger.Second != null ? trigger.Second.Value : -1;
|
||||
data.trigger.calendar.repeats = (byte)(trigger.Repeats ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case iOSNotificationTriggerType.Location:
|
||||
{
|
||||
var trigger = (iOSNotificationLocationTrigger)value;
|
||||
data.trigger.location.latitude = trigger.Latitude;
|
||||
data.trigger.location.longitude = trigger.Longitude;
|
||||
data.trigger.location.notifyOnEntry = (byte)(trigger.NotifyOnEntry ? 1 : 0);
|
||||
data.trigger.location.notifyOnExit = (byte)(trigger.NotifyOnExit ? 1 : 0);
|
||||
data.trigger.location.radius = trigger.Radius;
|
||||
data.trigger.location.repeats = (byte)(trigger.Repeats ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case iOSNotificationTriggerType.Push:
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown trigger type {value.Type}");
|
||||
}
|
||||
|
||||
data.triggerType = (int)value.Type;
|
||||
}
|
||||
|
||||
get
|
||||
{
|
||||
switch ((iOSNotificationTriggerType)data.triggerType)
|
||||
{
|
||||
case iOSNotificationTriggerType.TimeInterval:
|
||||
return new iOSNotificationTimeIntervalTrigger()
|
||||
{
|
||||
timeInterval = data.trigger.timeInterval.interval,
|
||||
Repeats = data.trigger.timeInterval.repeats != 0,
|
||||
};
|
||||
case iOSNotificationTriggerType.Calendar:
|
||||
{
|
||||
var trigger = new iOSNotificationCalendarTrigger()
|
||||
{
|
||||
Year = (data.trigger.calendar.year > 0) ? (int?)data.trigger.calendar.year : null,
|
||||
Month = (data.trigger.calendar.month > 0) ? (int?)data.trigger.calendar.month : null,
|
||||
Day = (data.trigger.calendar.day > 0) ? (int?)data.trigger.calendar.day : null,
|
||||
Hour = (data.trigger.calendar.hour >= 0) ? (int?)data.trigger.calendar.hour : null,
|
||||
Minute = (data.trigger.calendar.minute >= 0) ? (int?)data.trigger.calendar.minute : null,
|
||||
Second = (data.trigger.calendar.second >= 0) ? (int?)data.trigger.calendar.second : null,
|
||||
UtcTime = false,
|
||||
Repeats = data.trigger.calendar.repeats != 0
|
||||
};
|
||||
if (userInfo != null)
|
||||
{
|
||||
string utc;
|
||||
if (userInfo.TryGetValue("OriginalUtc", out utc))
|
||||
{
|
||||
if (utc == "1")
|
||||
trigger.UtcTime = true;
|
||||
}
|
||||
}
|
||||
return trigger;
|
||||
}
|
||||
case iOSNotificationTriggerType.Location:
|
||||
return new iOSNotificationLocationTrigger()
|
||||
{
|
||||
Latitude = data.trigger.location.latitude,
|
||||
Longitude = data.trigger.location.longitude,
|
||||
Radius = data.trigger.location.radius,
|
||||
NotifyOnEntry = data.trigger.location.notifyOnEntry != 0,
|
||||
NotifyOnExit = data.trigger.location.notifyOnExit != 0,
|
||||
Repeats = data.trigger.location.repeats != 0,
|
||||
};
|
||||
case iOSNotificationTriggerType.Push:
|
||||
return new iOSNotificationPushTrigger();
|
||||
default:
|
||||
throw new Exception($"Unknown trigger type {data.triggerType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateUniqueID()
|
||||
{
|
||||
return Math.Abs(DateTime.Now.ToString("yyMMddHHmmssffffff").GetHashCode()).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="iOSNotification"/> and automatically generate an unique string for <see cref="iOSNotification.Identifier"/> with all optional fields set to default values.
|
||||
/// </summary>
|
||||
public iOSNotification() : this(GenerateUniqueID())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specify a <see cref="iOSNotification.Identifier"/> and create a notification object with all optional fields set to default values.
|
||||
/// </summary>
|
||||
/// <param name="identifier"> Unique identifier for the local notification tha can later be used to track or change it's status.</param>
|
||||
public iOSNotification(string identifier)
|
||||
{
|
||||
data = new iOSNotificationData();
|
||||
data.identifier = identifier;
|
||||
data.title = "";
|
||||
data.body = "";
|
||||
data.badge = -1;
|
||||
data.subtitle = "";
|
||||
data.categoryIdentifier = "";
|
||||
data.threadIdentifier = "";
|
||||
|
||||
data.triggerType = -1;
|
||||
|
||||
data.userInfo = IntPtr.Zero;
|
||||
userInfo = new Dictionary<string, string>();
|
||||
Data = "";
|
||||
ShowInForeground = false;
|
||||
ForegroundPresentationOption = PresentationOption.Alert | PresentationOption.Sound;
|
||||
InterruptionLevel = NotificationInterruptionLevel.Active;
|
||||
RelevanceScore = 0;
|
||||
}
|
||||
|
||||
internal iOSNotification(iOSNotificationWithUserInfo data)
|
||||
{
|
||||
this.data = data.data;
|
||||
userInfo = data.userInfo;
|
||||
Attachments = data.attachments;
|
||||
}
|
||||
|
||||
iOSNotificationData data;
|
||||
Dictionary<string, string> userInfo;
|
||||
|
||||
internal iOSNotificationWithUserInfo GetDataForSending()
|
||||
{
|
||||
if (data.identifier == null)
|
||||
data.identifier = GenerateUniqueID();
|
||||
if (SoundVolume.HasValue)
|
||||
data.soundVolume = SoundVolume.Value;
|
||||
else
|
||||
data.soundVolume = -1.0f;
|
||||
|
||||
iOSNotificationWithUserInfo ret;
|
||||
ret.data = data;
|
||||
ret.userInfo = userInfo;
|
||||
ret.attachments = Attachments;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ab0873fd8f23f34681825b7642810ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for notification actions.
|
||||
/// These represent values from UNNotificationActionOptions.
|
||||
/// For more information, refer to <see href="https://developer.apple.com/documentation/usernotifications/unnotificationactionoptions">Apple documentation</see>.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum iOSNotificationActionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No specific action is performed.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// An action that requires the user to unlock their device.
|
||||
/// </summary>
|
||||
Required = (1 << 0),
|
||||
/// <summary>
|
||||
/// An irreversible action such as deleting data.
|
||||
/// </summary>
|
||||
Destructive = (1 << 1),
|
||||
/// <summary>
|
||||
/// An action that opens the application.
|
||||
/// </summary>
|
||||
Foreground = (1 << 2),
|
||||
}
|
||||
|
||||
enum iOSNotificationActionIconType
|
||||
{
|
||||
None = 0,
|
||||
SystemImageName = 1,
|
||||
TemplateImageName = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents action for an actionable notification.
|
||||
/// Actions are supposed to be added to notification categories, which are then registered prior to sending notifications.
|
||||
/// User can choose to tap a notification or one of associated actions. Application gets feedback of the choice.
|
||||
/// </summary>
|
||||
/// <seealso cref="iOSNotificationCategory"/>
|
||||
/// <seealso cref="iOSNotificationCenter.GetLastRespondedNotificationAction"/>
|
||||
public class iOSNotificationAction
|
||||
{
|
||||
internal iOSNotificationActionIconType _imageType;
|
||||
internal string _image;
|
||||
|
||||
/// <summary>
|
||||
/// An identifier for this action.
|
||||
/// Each action within an application unique must have unique ID.
|
||||
/// This ID will be returned by iOSNotificationCenter.GetLastRespondedNotificationAction if user chooses this action.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title for the action.
|
||||
/// This will be the title of the button that appears below the notification.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Options for the action. Can be a combination of given flags.
|
||||
/// Refer to Apple documentation for UNNotificationActionOptions for exact meanings.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationactionoptions"/>
|
||||
/// </summary>
|
||||
public iOSNotificationActionOptions Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the icon for action using system symbol image name.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationactionicon/3747241-iconwithsystemimagename?language=objc"/>
|
||||
/// </summary>
|
||||
public string SystemImageName
|
||||
{
|
||||
get { return _imageType == iOSNotificationActionIconType.SystemImageName ? _image : null; }
|
||||
set
|
||||
{
|
||||
_imageType = iOSNotificationActionIconType.SystemImageName;
|
||||
_image = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the icon for action using image from app's bundle.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationactionicon/3747242-iconwithtemplateimagename?language=objc"/>
|
||||
/// </summary>
|
||||
public string TemplateImageName
|
||||
{
|
||||
get { return _imageType == iOSNotificationActionIconType.TemplateImageName ? _image : null; }
|
||||
set
|
||||
{
|
||||
_imageType = iOSNotificationActionIconType.TemplateImageName;
|
||||
_image = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new action.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique identifier for this action</param>
|
||||
/// <param name="title">Title for the action (and button label)</param>
|
||||
public iOSNotificationAction(string id, string title)
|
||||
: this(id, title, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new action.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique identifier for this action</param>
|
||||
/// <param name="title">Title for the action (and button label)</param>
|
||||
/// <param name="options">Options for the action</param>
|
||||
public iOSNotificationAction(string id, string title, iOSNotificationActionOptions options)
|
||||
{
|
||||
Id = id;
|
||||
Title = title;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
internal virtual IntPtr CreateUNNotificationAction()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return iOSNotificationsWrapper._CreateUNNotificationAction(Id, Title, (int)Options, (int)_imageType, _image);
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a special notification action with text input support.
|
||||
/// Each action within an application unique must have unique ID.
|
||||
/// When user chooses this action, a prompt for text input appears.
|
||||
/// If this action is responded to by the user, iOSNotificationCenter.GetLastRespondedNotificationAction will return it's ID,
|
||||
/// while iOSNotificationCenter.GetLastRespondedNotificationUserText will return the text entered.
|
||||
/// </summary>
|
||||
public class iOSTextInputNotificationAction
|
||||
: iOSNotificationAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Text label for the button for submitting the text input.
|
||||
/// </summary>
|
||||
public string TextInputButtonTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The placeholder text for input.
|
||||
/// </summary>
|
||||
public string TextInputPlaceholder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates new text input action.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique identifier for this action</param>
|
||||
/// <param name="title">Title for the action (and button label)</param>
|
||||
/// <param name="buttonTitle">Label for a button for submitting the text input</param>
|
||||
public iOSTextInputNotificationAction(string id, string title, string buttonTitle)
|
||||
: base(id, title)
|
||||
{
|
||||
TextInputButtonTitle = buttonTitle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new text input action.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique identifier for this action</param>
|
||||
/// <param name="title">Title for the action (and button label)</param>
|
||||
/// <param name="options">Options for the action</param>
|
||||
/// <param name="buttonTitle">Label for a button for submitting the text input</param>
|
||||
public iOSTextInputNotificationAction(string id, string title, iOSNotificationActionOptions options, string buttonTitle)
|
||||
: base(id, title, options)
|
||||
{
|
||||
TextInputButtonTitle = buttonTitle;
|
||||
}
|
||||
|
||||
internal override IntPtr CreateUNNotificationAction()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return iOSNotificationsWrapper._CreateUNTextInputNotificationAction(Id, Title, (int)Options, (int)_imageType, _image, TextInputButtonTitle, TextInputPlaceholder);
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7d19fa19fc06456eac4adb06cbf32e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Notification attachment.
|
||||
/// Refer to Apple documentation for details.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationattachment?language=objc"/>
|
||||
/// </summary>
|
||||
public struct iOSNotificationAttachment
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique identifier for the attachments. Will be auto-generated if left empty.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL to local file, accessible to the application.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// attachmend.Url = new System.Uri(System.IO.Path.Combine(Application.streamingAssetsPath, fileName)).AbsoluteUri;
|
||||
/// </code>
|
||||
/// </example>
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6bc2d6d64e6740d78bacee53461b615
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for notification category. Multiple options can be combined.
|
||||
/// These represent values from UNNotificationCategoryOptions.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcategoryoptions"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum iOSNotificationCategoryOptions
|
||||
{
|
||||
None = 0,
|
||||
CustomDismissAction = (1 << 0),
|
||||
AllowInCarPlay = (1 << 1),
|
||||
HiddenPreviewsShowTitle = (1 << 2),
|
||||
HiddenPreviewsShowSubtitle = (1 << 3),
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents notification category.
|
||||
/// Notification categories need to be registered on application start to be useful.
|
||||
/// By adding actions to category, you make all notification sent with this category identifier actionable.
|
||||
/// </summary>
|
||||
/// <seealso cref="iOSNotification.CategoryIdentifier"/>
|
||||
/// <seealso cref="https://developer.apple.com/documentation/usernotifications/unnotificationcategory"/>
|
||||
public class iOSNotificationCategory
|
||||
{
|
||||
List<iOSNotificationAction> m_Actions = new List<iOSNotificationAction>();
|
||||
List<string> m_IntentIdentifiers = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// A unique identifier for this category.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get actions set for this category.
|
||||
/// For more info see <see cref="iOSNotificationAction"/>.
|
||||
/// </summary>
|
||||
public iOSNotificationAction[] Actions { get { return m_Actions.ToArray(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Intent identifiers set for this category.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcategory/1649282-intentidentifiers"/>
|
||||
/// </summary>
|
||||
public string[] IntentIdentifiers { get { return m_IntentIdentifiers.ToArray(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The placeholder text to display when the system disables notification previews for the app.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcategory/2873736-hiddenpreviewsbodyplaceholder"/>
|
||||
/// </summary>
|
||||
public string HiddenPreviewsBodyPlaceholder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A format string for the summary description used when the system groups the category’s notifications.
|
||||
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationcategory/2963112-categorysummaryformat"/>
|
||||
/// </summary>
|
||||
public string SummaryFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Options for how to handle notifications of this type.
|
||||
/// </summary>
|
||||
public iOSNotificationCategoryOptions Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create notification category.
|
||||
/// Category must be registered using iOSNotificationCenter.SetNotificationCategories.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for this category</param>
|
||||
public iOSNotificationCategory(string id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create notification category.
|
||||
/// Category must be registered using iOSNotificationCenter.SetNotificationCategories.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for this category</param>
|
||||
/// <param name="actions">Add provided actions to this category</param>
|
||||
public iOSNotificationCategory(string id, IEnumerable<iOSNotificationAction> actions)
|
||||
: this(id)
|
||||
{
|
||||
if (actions != null)
|
||||
m_Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create notification category.
|
||||
/// Category must be registered using iOSNotificationCenter.SetNotificationCategories.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for this category</param>
|
||||
/// <param name="actions">Add provided actions to this category</param>
|
||||
/// <param name="intentIdentifiers">Add provided intent identifiers to this category</param>
|
||||
public iOSNotificationCategory(string id, IEnumerable<iOSNotificationAction> actions, IEnumerable<string> intentIdentifiers)
|
||||
: this(id, actions)
|
||||
{
|
||||
if (intentIdentifiers != null)
|
||||
m_IntentIdentifiers.AddRange(intentIdentifiers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add action to this category.
|
||||
/// Actions must be added prior to registering the category.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to add</param>
|
||||
public void AddAction(iOSNotificationAction action)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentException("Cannot add null action");
|
||||
m_Actions.Add(action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add actions to this category.
|
||||
/// Actions must be added prior to registering the category.
|
||||
/// </summary>
|
||||
/// <param name="actions">Actions to add</param>
|
||||
public void AddActions(IEnumerable<iOSNotificationAction> actions)
|
||||
{
|
||||
if (actions == null)
|
||||
throw new ArgumentException("Cannot add null actions collection");
|
||||
m_Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add intent identifier to this category.
|
||||
/// Intent identifiers must be added prior to registering the category.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Intent identifier to add</param>
|
||||
public void AddIntentIdentifier(string identifier)
|
||||
{
|
||||
if (identifier == null)
|
||||
throw new ArgumentException("Cannot add null intent identifier");
|
||||
m_IntentIdentifiers.Add(identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add intent identifier to this category.
|
||||
/// Intent identifiers must be added prior to registering the category.
|
||||
/// </summary>
|
||||
/// <param name="identifiers">Intent identifiers to add</param>
|
||||
public void AddIntentIdentifiers(IEnumerable<string> identifiers)
|
||||
{
|
||||
if (identifiers == null)
|
||||
throw new ArgumentException("Cannot add null intent identifiers collection");
|
||||
m_IntentIdentifiers.AddRange(identifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f323c7093dfc41d587ecb64426dceb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the iOSNotificationCenter to register notification channels and schedule local notifications.
|
||||
/// </summary>
|
||||
public class iOSNotificationCenter
|
||||
{
|
||||
private static bool s_Initialized;
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type for the notification received callbacks.
|
||||
/// </summary>
|
||||
public delegate void NotificationReceivedCallback(iOSNotification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to this event to receive a callback whenever a local notification or a remote is shown to the user.
|
||||
/// </summary>
|
||||
public static event NotificationReceivedCallback OnNotificationReceived
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!s_OnNotificationReceivedCallbackSet)
|
||||
{
|
||||
iOSNotificationsWrapper.RegisterOnReceivedCallback();
|
||||
s_OnNotificationReceivedCallbackSet = true;
|
||||
}
|
||||
|
||||
s_OnNotificationReceived += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
s_OnNotificationReceived -= value;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool s_OnNotificationReceivedCallbackSet;
|
||||
private static event NotificationReceivedCallback s_OnNotificationReceived = delegate { };
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to this event to receive a callback whenever a remote notification is received while the app is in foreground,
|
||||
/// if you subscribe to this event remote notification will not be shown while the app is in foreground and if you still want
|
||||
/// to show it to the user you will have to schedule a local notification with the data received from this callback.
|
||||
/// If you want remote notifications to be shown automatically subscribe to the [[OnNotificationReceived]] even instead and check the
|
||||
/// [[Notification.Trigger]] class type to determine whether the received notification is a remote notification.
|
||||
/// </summary>
|
||||
public static event NotificationReceivedCallback OnRemoteNotificationReceived
|
||||
{
|
||||
add
|
||||
{
|
||||
if (!s_OnRemoteNotificationReceivedCallbackSet)
|
||||
{
|
||||
iOSNotificationsWrapper.RegisterOnReceivedRemoteNotificationCallback();
|
||||
s_OnRemoteNotificationReceivedCallbackSet = true;
|
||||
}
|
||||
|
||||
s_OnRemoteNotificationReceived += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
s_OnRemoteNotificationReceived -= value;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool s_OnRemoteNotificationReceivedCallbackSet;
|
||||
private static event NotificationReceivedCallback s_OnRemoteNotificationReceived = delegate { };
|
||||
|
||||
internal delegate void AuthorizationRequestCompletedCallback(iOSAuthorizationRequestData data);
|
||||
|
||||
/// <summary>
|
||||
/// The number currently set as the badge of the app icon.
|
||||
/// </summary>
|
||||
public static int ApplicationBadge
|
||||
{
|
||||
get { return iOSNotificationsWrapper.GetApplicationBadge(); }
|
||||
set { iOSNotificationsWrapper.SetApplicationBadge(value); }
|
||||
}
|
||||
|
||||
static bool Initialize()
|
||||
{
|
||||
#if UNITY_EDITOR || !UNITY_IOS
|
||||
return false;
|
||||
#elif UNITY_IOS
|
||||
|
||||
if (s_Initialized)
|
||||
return true;
|
||||
|
||||
iOSNotificationsWrapper.RegisterOnReceivedCallback();
|
||||
return s_Initialized = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a local notification for delivery.
|
||||
/// </summary>
|
||||
/// <param name="notification">Notification to schedule</param>
|
||||
public static void ScheduleNotification(iOSNotification notification)
|
||||
{
|
||||
if (!Initialize())
|
||||
return;
|
||||
|
||||
iOSNotificationsWrapper.ScheduleLocalNotification(notification.GetDataForSending());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all notifications that are currently scheduled.
|
||||
/// </summary>
|
||||
/// <returns>Array of scheduled notifications</returns>
|
||||
public static iOSNotification[] GetScheduledNotifications()
|
||||
{
|
||||
return NotificationDataToNotifications(iOSNotificationsWrapper.GetScheduledNotificationData());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the app's delivered notifications that are currently shown in the Notification Center.
|
||||
/// </summary>
|
||||
/// <returns>Array of delivered notifications</returns>
|
||||
public static iOSNotification[] GetDeliveredNotifications()
|
||||
{
|
||||
return NotificationDataToNotifications(iOSNotificationsWrapper.GetDeliveredNotificationData());
|
||||
}
|
||||
|
||||
private static iOSNotification[] NotificationDataToNotifications(iOSNotificationWithUserInfo[] notificationData)
|
||||
{
|
||||
var iOSNotifications = new iOSNotification[notificationData == null ? 0 : notificationData.Length];
|
||||
|
||||
for (int i = 0; i < iOSNotifications.Length; ++i)
|
||||
iOSNotifications[i] = new iOSNotification(notificationData[i]);
|
||||
|
||||
return iOSNotifications;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this to retrieve the last local or remote notification received by the app.
|
||||
/// Do not call this in Awake or Start of the first scene, wait for at least a frame.
|
||||
/// On cold app start iOS reports this with small delay.
|
||||
/// </summary>
|
||||
/// <seealso cref="SetNotificationCategories(IEnumerable{iOSNotificationCategory})"/>
|
||||
/// <returns>
|
||||
/// Returns the last local or remote notification used to open the app or clicked on by the user. If no notification is available it returns null.
|
||||
/// </returns>
|
||||
public static iOSNotification GetLastRespondedNotification()
|
||||
{
|
||||
var data = iOSNotificationsWrapper.GetLastNotificationData();
|
||||
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
return new iOSNotification(data.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get users chosen action for the last actionable notification, null if no action was chosen.
|
||||
/// </summary>
|
||||
/// <seealso cref="SetNotificationCategories(IEnumerable{iOSNotificationCategory})"/>
|
||||
/// <returns>Action identifier</returns>
|
||||
public static string GetLastRespondedNotificationAction()
|
||||
{
|
||||
return iOSNotificationsWrapper.GetLastRespondedNotificationAction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get users text input for the last actionable notification with input support, null if no input.
|
||||
/// </summary>
|
||||
/// <returns>Text user extered in the input field from notification</returns>
|
||||
public static string GetLastRespondedNotificationUserText()
|
||||
{
|
||||
return iOSNotificationsWrapper.GetLastRespondedNotificationUserText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unschedules the specified notification.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier for the notification to be removed</param>
|
||||
public static void RemoveScheduledNotification(string identifier)
|
||||
{
|
||||
if (Initialize())
|
||||
iOSNotificationsWrapper._RemoveScheduledNotification(identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified notification from Notification Center.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier for the notification to be removed</param>
|
||||
public static void RemoveDeliveredNotification(string identifier)
|
||||
{
|
||||
if (Initialize())
|
||||
iOSNotificationsWrapper._RemoveDeliveredNotification(identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unschedules all pending notification.
|
||||
/// </summary>
|
||||
public static void RemoveAllScheduledNotifications()
|
||||
{
|
||||
if (Initialize())
|
||||
iOSNotificationsWrapper._RemoveAllScheduledNotifications();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all of the app's delivered notifications from the Notification Center.
|
||||
/// </summary>
|
||||
public static void RemoveAllDeliveredNotifications()
|
||||
{
|
||||
if (Initialize())
|
||||
iOSNotificationsWrapper._RemoveAllDeliveredNotifications();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the notification settings for this app.
|
||||
/// </summary>
|
||||
/// <returns>Notification settings</returns>
|
||||
public static iOSNotificationSettings GetNotificationSettings()
|
||||
{
|
||||
return iOSNotificationsWrapper.GetNotificationSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set (replace if already set) notification categories.
|
||||
/// Use this to setup actionable notifications. You can specify actions for each category,
|
||||
/// which then will be available for each notification with the same category identifier.
|
||||
/// Categories must be registered before sending notifications.
|
||||
/// </summary>
|
||||
/// <param name="categories">All notification categories for your application</param>
|
||||
public static void SetNotificationCategories(IEnumerable<iOSNotificationCategory> categories)
|
||||
{
|
||||
iOSNotificationsWrapper.SetNotificationCategories(categories);
|
||||
}
|
||||
|
||||
internal static void OnReceivedRemoteNotification(iOSNotificationWithUserInfo data)
|
||||
{
|
||||
var notification = new iOSNotification(data);
|
||||
s_OnRemoteNotificationReceived(notification);
|
||||
}
|
||||
|
||||
internal static void OnSentNotification(iOSNotificationWithUserInfo data)
|
||||
{
|
||||
var notification = new iOSNotification(data);
|
||||
s_OnNotificationReceived(notification);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens Settings.
|
||||
/// On iOS there is no way to open notification settings specifically, but you can open settings app with current application settings.
|
||||
/// Note, that application will be suspended, since opening settings is switching to different application.
|
||||
/// </summary>
|
||||
public static void OpenNotificationSettings()
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
iOSNotificationsWrapper._OpenNotificationSettings();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a54aa2e66430c45c2b076910262347d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum indicating whether the app is allowed to schedule notifications.
|
||||
/// You can capture these values in <see cref="iOSNotificationSettings.AuthorizationStatus"/> property using <see cref="iOSNotificationCenter.GetNotificationSettings"/>method.
|
||||
/// </summary>
|
||||
public enum AuthorizationStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The user has not yet made a choice regarding whether the application may post notifications.
|
||||
/// </summary>
|
||||
NotDetermined = 0,
|
||||
/// <summary>
|
||||
/// The application is not authorized to post notifications.
|
||||
/// </summary>
|
||||
Denied = 1,
|
||||
/// <summary>
|
||||
/// The application is authorized to post notifications.
|
||||
/// </summary>
|
||||
Authorized = 2,
|
||||
/// <summary>
|
||||
/// The application is authorized to post non-interruptive user notifications.
|
||||
/// </summary>
|
||||
Provisional = 3,
|
||||
/// <summary>
|
||||
/// The application is temporarily authorized to post notifications. Only available to app clips.
|
||||
/// </summary>
|
||||
Ephemeral = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Presentation styles for alerts.
|
||||
/// </summary>
|
||||
public enum AlertStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// No alert.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Banner alerts.
|
||||
/// </summary>
|
||||
Banner = 1,
|
||||
/// <summary>
|
||||
/// Modal alerts.
|
||||
/// </summary>
|
||||
Alert = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The style for previewing a notification's content.
|
||||
/// </summary>
|
||||
public enum ShowPreviewsSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// The notification's content is always shown, even when the device is locked.
|
||||
/// </summary>
|
||||
Always = 0,
|
||||
/// <summary>
|
||||
/// The notification's content is shown only when the device is unlocked.
|
||||
/// </summary>
|
||||
WhenAuthenticated = 1,
|
||||
/// <summary>
|
||||
/// The notification's content is never shown, even when the device is unlocked
|
||||
/// </summary>
|
||||
Never = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum indicating the current status of a notification setting.
|
||||
/// </summary>
|
||||
public enum NotificationSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// The app does not support this notification setting.
|
||||
/// </summary>
|
||||
NotSupported = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The notification setting is turned off.
|
||||
/// </summary>
|
||||
Disabled,
|
||||
|
||||
/// <summary>
|
||||
/// The notification setting is turned on.
|
||||
/// </summary>
|
||||
Enabled,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// iOSNotificationSettings contains the current authorization status and notification-related settings for your app. Your app must receive authorization to schedule notifications.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this struct to determine what notification-related actions your app is allowed to perform by the user. This information should be used to enable, disable, or adjust your app's notification-related behaviors.
|
||||
/// The system enforces your app's settings by preventing denied interactions from occurring.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct iOSNotificationSettings
|
||||
{
|
||||
internal int authorizationStatus;
|
||||
internal int notificationCenterSetting;
|
||||
internal int lockScreenSetting;
|
||||
internal int carPlaySetting;
|
||||
internal int alertSetting;
|
||||
internal int badgeSetting;
|
||||
internal int soundSetting;
|
||||
|
||||
internal int alertStyle;
|
||||
internal int showPreviewsSetting;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When the value is set to Authorized your app is allowed to schedule and receive local and remote notifications.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When authorized, use the alertSetting, badgeSetting, and soundSetting properties to specify which types of interactions are allowed.
|
||||
/// When the `AuthorizationStatus` value is `Denied`, the system doesn't deliver notifications to your app, and the system ignores any attempts to schedule local notifications.
|
||||
/// </remarks>
|
||||
public AuthorizationStatus AuthorizationStatus
|
||||
{
|
||||
get { return (AuthorizationStatus)authorizationStatus; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The setting that indicates whether your app’s notifications are displayed in Notification Center.
|
||||
/// </summary>
|
||||
public NotificationSetting NotificationCenterSetting
|
||||
{
|
||||
get { return (NotificationSetting)notificationCenterSetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The setting that indicates whether your app’s notifications appear onscreen when the device is locked.
|
||||
/// </summary>
|
||||
public NotificationSetting LockScreenSetting
|
||||
{
|
||||
get { return (NotificationSetting)lockScreenSetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The setting that indicates whether your app’s notifications may be displayed in a CarPlay environment.
|
||||
/// </summary>
|
||||
public NotificationSetting CarPlaySetting
|
||||
{
|
||||
get { return (NotificationSetting)carPlaySetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The authorization status for displaying alerts.
|
||||
/// </summary>
|
||||
public NotificationSetting AlertSetting
|
||||
{
|
||||
get { return (NotificationSetting)alertSetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The authorization status for badging your app’s icon.
|
||||
/// </summary>
|
||||
public NotificationSetting BadgeSetting
|
||||
{
|
||||
get { return (NotificationSetting)badgeSetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The authorization status for playing sounds for incoming notifications.
|
||||
/// </summary>
|
||||
public NotificationSetting SoundSetting
|
||||
{
|
||||
get { return (NotificationSetting)soundSetting; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of alert that the app may display when the device is unlocked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property specifies the presentation style for alerts when the device is unlocked.
|
||||
/// The user may choose to display alerts as automatically disappearing banners or as modal windows that require explicit dismissal (the user may also choose not to display alerts at all.
|
||||
/// </remarks>
|
||||
public AlertStyle AlertStyle
|
||||
{
|
||||
get { return (AlertStyle)alertStyle; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The setting that indicates whether the app shows a preview of the notification's content.
|
||||
/// </summary>
|
||||
public ShowPreviewsSetting ShowPreviewsSetting
|
||||
{
|
||||
get { return (ShowPreviewsSetting)showPreviewsSetting; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f0fce3da9e80904c834cd6c21a9fc08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes notification trigger type
|
||||
/// </summary>
|
||||
public enum iOSNotificationTriggerType
|
||||
{
|
||||
/// <summary>
|
||||
/// Time interval trigger
|
||||
/// </summary>
|
||||
TimeInterval = 0,
|
||||
/// <summary>
|
||||
/// Calendar trigger
|
||||
/// </summary>
|
||||
Calendar = 10,
|
||||
/// <summary>
|
||||
/// Location trigger
|
||||
/// </summary>
|
||||
Location = 20,
|
||||
/// <summary>
|
||||
/// Push notification trigger
|
||||
/// </summary>
|
||||
Push = 3,
|
||||
/// <summary>
|
||||
/// Trigger, that is not known to this version of notifications package
|
||||
/// </summary>
|
||||
Unknown = -1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// iOSNotificationTrigger interface is implemented by notification trigger types representing an event that triggers the delivery of a notification.
|
||||
/// </summary>
|
||||
public interface iOSNotificationTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the trigger type for this trigger.
|
||||
/// </summary>
|
||||
iOSNotificationTriggerType Type { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A trigger condition that causes a notification to be delivered when the user's device enters or exits the specified geographic region.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a UNLocationNotificationTrigger instance when you want to schedule the delivery of a local notification when the device enters or leaves a specific geographic region.
|
||||
/// The system limits the number of location-based triggers that may be scheduled at the same time. Before scheduling any notifications using this trigger,
|
||||
/// your app must have authorization to use Core Location and must have when-in-use permissions. Use the Unity LocationService API to request for this authorization.
|
||||
/// Region-based notifications aren't always triggered immediately when the edge of the boundary is crossed. The system applies heuristics to ensure that the boundary crossing
|
||||
/// represents a deliberate event and is not the result of spurious location data.
|
||||
/// See https://developer.apple.com/documentation/corelocation/clregion?language=objc for additional information.
|
||||
///</remarks>
|
||||
public struct iOSNotificationLocationTrigger : iOSNotificationTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of notification trigger.
|
||||
/// </summary>
|
||||
public iOSNotificationTriggerType Type { get { return iOSNotificationTriggerType.Location; } }
|
||||
|
||||
/// <summary>
|
||||
/// The center point of the geographic area.
|
||||
/// </summary>
|
||||
[Obsolete("Use Latitude and Longitude", false)]
|
||||
public Vector2 Center
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2((float)Latitude, (float)Longitude);
|
||||
}
|
||||
set
|
||||
{
|
||||
Latitude = value.x;
|
||||
Longitude = value.y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The latitude of the center point of the geographic area.
|
||||
/// </summary>
|
||||
public double Latitude { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The longitude of the center point of the geographic area.
|
||||
/// </summary>
|
||||
public double Longitude { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The radius (measured in meters) that defines the geographic area’s outer boundary.
|
||||
/// </summary>
|
||||
public float Radius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this property is enabled, a device crossing from outside the region to inside the region triggers the delivery of a notification
|
||||
/// </summary>
|
||||
public bool NotifyOnEntry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this property is enabled, a device crossing from inside the region to outside the region triggers the delivery of a notification
|
||||
/// </summary>
|
||||
public bool NotifyOnExit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the notification should repeat.
|
||||
/// </summary>
|
||||
public bool Repeats { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A trigger condition that indicates the notification was sent from Apple Push Notification Service (APNs).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should not create instances of this type manually. Instead compare the Trigger property of notification objects received in `OnNotificationReceived` to this type to
|
||||
/// determine whether the received notification was scheduled locally or remotely.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// notification.Trigger is iOSNotificationPushTrigger
|
||||
/// </example>
|
||||
|
||||
public struct iOSNotificationPushTrigger : iOSNotificationTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of notification trigger.
|
||||
/// </summary>
|
||||
public iOSNotificationTriggerType Type { get { return iOSNotificationTriggerType.Push; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A trigger condition that causes a notification to be delivered after the specified amount of time elapses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create a iOSNotificationTimeIntervalTrigger instance when you want to schedule the delivery of a local notification after the specified time span has elapsed.
|
||||
/// </remarks>
|
||||
public struct iOSNotificationTimeIntervalTrigger : iOSNotificationTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of notification trigger.
|
||||
/// </summary>
|
||||
public iOSNotificationTriggerType Type { get { return iOSNotificationTriggerType.TimeInterval; } }
|
||||
|
||||
internal int timeInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Time interval after which the notification should be delivered (only total of full seconds is considered).
|
||||
/// </summary>
|
||||
public TimeSpan TimeInterval
|
||||
{
|
||||
get { return TimeSpan.FromSeconds(timeInterval); }
|
||||
set
|
||||
{
|
||||
timeInterval = (int)value.TotalSeconds;
|
||||
if (timeInterval <= 0)
|
||||
throw new ArgumentException("Time interval must be greater than 0.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the notification should repeat.
|
||||
/// </summary>
|
||||
public bool Repeats { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A trigger condition that causes a notification to be delivered at a specific date and time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Create an instance of <see cref="iOSNotificationCalendarTrigger"/> when you want to schedule the delivery of a local notification at the specified date and time.
|
||||
/// You are not required to set all of the fields because the system uses the provided information to determine the next date and time that matches the specified information automatically.
|
||||
/// </remarks>
|
||||
public struct iOSNotificationCalendarTrigger : iOSNotificationTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of notification trigger.
|
||||
/// </summary>
|
||||
public iOSNotificationTriggerType Type { get { return iOSNotificationTriggerType.Calendar; } }
|
||||
|
||||
/// <summary>
|
||||
/// Year
|
||||
/// </summary>
|
||||
public int? Year { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Month
|
||||
/// </summary>
|
||||
public int? Month { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Day
|
||||
/// </summary>
|
||||
public int? Day { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hour
|
||||
/// </summary>
|
||||
public int? Hour { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minute
|
||||
/// </summary>
|
||||
public int? Minute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Second
|
||||
/// </summary>
|
||||
public int? Second { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Are Date and Time field in UTC time. When false, use local time.
|
||||
/// </summary>
|
||||
public bool UtcTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicate whether the notification is repeated every defined time period. For instance if hour and minute fields are set the notification will be triggered every day at the specified hour and minute.
|
||||
/// </summary>
|
||||
public bool Repeats { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts this trigger into the one using UTC time.
|
||||
/// </summary>
|
||||
/// <returns>A new trigger with UtcTime set to true and other field adjusted accordingly.</returns>
|
||||
public iOSNotificationCalendarTrigger ToUtc()
|
||||
{
|
||||
if (UtcTime)
|
||||
return this;
|
||||
|
||||
var notificationTime = AssignDateTimeComponents(DateTime.Now).ToUniversalTime();
|
||||
iOSNotificationCalendarTrigger result = this;
|
||||
result.UtcTime = true;
|
||||
result.AssignNonEmptyComponents(notificationTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this trigger into the one using local time.
|
||||
/// </summary>
|
||||
/// <returns>A new trigger with UtcTime set to false and other field adjusted accordingly.</returns>
|
||||
public iOSNotificationCalendarTrigger ToLocal()
|
||||
{
|
||||
if (!UtcTime)
|
||||
return this;
|
||||
|
||||
var notificationTime = AssignDateTimeComponents(DateTime.UtcNow).ToLocalTime();
|
||||
iOSNotificationCalendarTrigger result = this;
|
||||
result.UtcTime = false;
|
||||
result.AssignNonEmptyComponents(notificationTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal DateTime AssignDateTimeComponents(DateTime dt)
|
||||
{
|
||||
int year = Year != null ? Year.Value : dt.Year;
|
||||
int month = Month != null ? Month.Value : dt.Month;
|
||||
int day = Day != null ? Day.Value : dt.Day;
|
||||
int hour = Hour != null ? Hour.Value : dt.Hour;
|
||||
int minute = Minute != null ? Minute.Value : dt.Minute;
|
||||
int second = Second != null ? Second.Value : dt.Second;
|
||||
return new DateTime(year, month, day, hour, minute, second, dt.Kind);
|
||||
}
|
||||
|
||||
internal void AssignNonEmptyComponents(DateTime dt)
|
||||
{
|
||||
if (Year != null)
|
||||
Year = dt.Year;
|
||||
if (Month != null)
|
||||
Month = dt.Month;
|
||||
if (Day != null)
|
||||
Day = dt.Day;
|
||||
if (Hour != null)
|
||||
Hour = dt.Hour;
|
||||
if (Minute != null)
|
||||
Minute = dt.Minute;
|
||||
if (Second != null)
|
||||
Second = dt.Second;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56be22c2d38b781439f9da4ef07e2638
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 162
|
||||
|
||||
namespace Unity.Notifications.iOS
|
||||
{
|
||||
internal struct iOSNotificationWithUserInfo
|
||||
{
|
||||
internal iOSNotificationData data;
|
||||
internal Dictionary<string, string> userInfo;
|
||||
internal List<iOSNotificationAttachment> attachments;
|
||||
}
|
||||
|
||||
internal class iOSNotificationsWrapper : MonoBehaviour
|
||||
{
|
||||
#if DEVELOPMENT_BUILD
|
||||
[DllImport("__Internal")]
|
||||
private static extern int _NativeSizeof_iOSNotificationAuthorizationData();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern int _NativeSizeof_iOSNotificationData();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern int _NativeSizeof_NotificationSettingsData();
|
||||
#endif
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _RequestAuthorization(IntPtr request, Int32 options, bool registerForRemote);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern int _RegisteredForRemoteNotifications();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _UnregisterForRemoteNotifications();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _ScheduleLocalNotification(iOSNotificationData data);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _SetNotificationReceivedDelegate(NotificationReceivedCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _SetRemoteNotificationReceivedDelegate(NotificationReceivedCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _SetAuthorizationRequestReceivedDelegate(AuthorizationRequestCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern iOSNotificationSettings _GetNotificationSettings();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _GetScheduledNotificationDataArray(out Int32 count);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _GetDeliveredNotificationDataArray(out Int32 count);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern void _RemoveScheduledNotification(string identifier);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern void _RemoveAllScheduledNotifications();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern void _RemoveDeliveredNotification(string identifier);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _SetApplicationBadge(Int32 badge);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern Int32 _GetApplicationBadge();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern bool _GetAppOpenedUsingNotification();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern void _RemoveAllDeliveredNotifications();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _GetLastNotificationData();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern string _GetLastRespondedNotificationAction();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern string _GetLastRespondedNotificationUserText();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _FreeUnmanagediOSNotificationDataArray(IntPtr ptr, int count);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern IntPtr _AddItemToNSDictionary(IntPtr dict, string key, string value);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern IntPtr _AddAttachmentToNSArray(IntPtr atts, string id, string url, out IntPtr error);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _ReadNSDictionary(IntPtr handle, IntPtr nsDict, ReceiveNSDictionaryKeyValueCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _ReadAttachmentsNSArray(IntPtr handle, IntPtr nsArray, ReceiveUNNotificationAttachmentCallback callback);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern IntPtr _CreateUNNotificationAction(string id, string title, int options, int iconType, string icon);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern IntPtr _CreateUNTextInputNotificationAction(string id, string title, int options, int iconType, string icon, string buttonTitle, string placeholder);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _ReleaseNSObject(IntPtr obj);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern string _NSErrorToMessage(IntPtr error);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _AddActionToNSArray(IntPtr actions, IntPtr action, int capacity);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _CreateUNNotificationCategory(string id, string hiddenPreviewsBodyPlaceholder, string summaryFormat, int options, IntPtr actions, IntPtr intentIdentifiers);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _AddCategoryToCategorySet(IntPtr categorySet, IntPtr category);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _SetNotificationCategories(IntPtr categorySet);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern IntPtr _AddStringToNSArray(IntPtr array, string str, int capacity);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
internal static extern void _OpenNotificationSettings();
|
||||
|
||||
private delegate void AuthorizationRequestCallback(IntPtr request, iOSAuthorizationRequestData data);
|
||||
private delegate void NotificationReceivedCallback(iOSNotificationData notificationData);
|
||||
private delegate void ReceiveNSDictionaryKeyValueCallback(IntPtr dict, string key, string value);
|
||||
private delegate void ReceiveUNNotificationAttachmentCallback(IntPtr array, string id, string url);
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR && DEVELOPMENT_BUILD
|
||||
static iOSNotificationsWrapper()
|
||||
{
|
||||
VerifyNativeManagedSize(_NativeSizeof_iOSNotificationAuthorizationData(), typeof(iOSAuthorizationRequestData));
|
||||
VerifyNativeManagedSize(_NativeSizeof_iOSNotificationData(), typeof(iOSNotificationData));
|
||||
VerifyNativeManagedSize(_NativeSizeof_NotificationSettingsData(), typeof(iOSNotificationSettings));
|
||||
}
|
||||
|
||||
static void VerifyNativeManagedSize(int nativeSize, Type managedType)
|
||||
{
|
||||
var managedSize = Marshal.SizeOf(managedType);
|
||||
if (nativeSize != managedSize)
|
||||
throw new Exception(string.Format("Native/managed struct size missmatch: {0} vs {1}", nativeSize, managedSize));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static void RegisterAuthorizationRequestCallback()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_SetAuthorizationRequestReceivedDelegate(AuthorizationRequestReceived);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void RegisterOnReceivedRemoteNotificationCallback()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_SetRemoteNotificationReceivedDelegate(RemoteNotificationReceived);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void RegisterOnReceivedCallback()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_SetNotificationReceivedDelegate(NotificationReceived);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(AuthorizationRequestCallback))]
|
||||
public static void AuthorizationRequestReceived(IntPtr request, iOSAuthorizationRequestData data)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
AuthorizationRequest.OnAuthorizationRequestCompleted(request, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(NotificationReceivedCallback))]
|
||||
public static void RemoteNotificationReceived(iOSNotificationData data)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
iOSNotificationCenter.OnReceivedRemoteNotification(NotificationDataToDataWithUserInfo(data));
|
||||
#endif
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(NotificationReceivedCallback))]
|
||||
public static void NotificationReceived(iOSNotificationData data)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
iOSNotificationCenter.OnSentNotification(NotificationDataToDataWithUserInfo(data));
|
||||
#endif
|
||||
}
|
||||
|
||||
static iOSNotificationWithUserInfo NotificationDataToDataWithUserInfo(iOSNotificationData data)
|
||||
{
|
||||
iOSNotificationWithUserInfo ret;
|
||||
ret.data = data;
|
||||
ret.data.userInfo = IntPtr.Zero;
|
||||
ret.userInfo = NSDictionaryToCs(data.userInfo);
|
||||
ret.attachments = AttachmentsNSArrayToCs(data.attachments);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(ReceiveNSDictionaryKeyValueCallback))]
|
||||
private static void ReceiveNSDictionaryKeyValue(IntPtr dict, string key, string value)
|
||||
{
|
||||
GCHandle handle = GCHandle.FromIntPtr(dict);
|
||||
var dictionary = (Dictionary<string, string>)handle.Target;
|
||||
if (dictionary == null)
|
||||
return;
|
||||
dictionary[key] = value;
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(ReceiveUNNotificationAttachmentCallback))]
|
||||
private static void ReceiveUNNotificationAttachment(IntPtr array, string id, string url)
|
||||
{
|
||||
GCHandle handle = GCHandle.FromIntPtr(array);
|
||||
var list = (List<iOSNotificationAttachment>)handle.Target;
|
||||
if (list == null)
|
||||
return;
|
||||
list.Add(new iOSNotificationAttachment()
|
||||
{
|
||||
Id = id,
|
||||
Url = url,
|
||||
});
|
||||
}
|
||||
|
||||
public static void RequestAuthorization(IntPtr request, int options, bool registerRemote)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_RequestAuthorization(request, options, registerRemote);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool RegisteredForRemoteNotifications()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _RegisteredForRemoteNotifications() != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void UnregisterForRemoteNotifications()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_UnregisterForRemoteNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static iOSNotificationSettings GetNotificationSettings()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _GetNotificationSettings();
|
||||
#else
|
||||
return new iOSNotificationSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void ScheduleLocalNotification(iOSNotificationWithUserInfo data)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
data.data.userInfo = iOSNotificationsWrapper.CsDictionaryToObjC(data.userInfo);
|
||||
data.data.attachments = iOSNotificationsWrapper.CsAttachmentsToObjc(data.attachments);
|
||||
_ScheduleLocalNotification(data.data);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static iOSNotificationWithUserInfo[] GetDeliveredNotificationData()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
int count;
|
||||
var ptr = _GetDeliveredNotificationDataArray(out count);
|
||||
return MarshalAndFreeNotificationDataArray(ptr, count);
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string GetLastRespondedNotificationAction()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _GetLastRespondedNotificationAction();
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string GetLastRespondedNotificationUserText()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _GetLastRespondedNotificationUserText();
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static iOSNotificationWithUserInfo[] GetScheduledNotificationData()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
int count;
|
||||
var ptr = _GetScheduledNotificationDataArray(out count);
|
||||
return MarshalAndFreeNotificationDataArray(ptr, count);
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
static iOSNotificationWithUserInfo[] MarshalAndFreeNotificationDataArray(IntPtr ptr, int count)
|
||||
{
|
||||
if (count == 0 || ptr == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
var dataArray = new iOSNotificationWithUserInfo[count];
|
||||
var structSize = Marshal.SizeOf(typeof(iOSNotificationData));
|
||||
var next = ptr;
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
dataArray[i].data = (iOSNotificationData)Marshal.PtrToStructure(next, typeof(iOSNotificationData));
|
||||
dataArray[i].userInfo = NSDictionaryToCs(dataArray[i].data.userInfo);
|
||||
dataArray[i].attachments = AttachmentsNSArrayToCs(dataArray[i].data.attachments);
|
||||
next = next + structSize;
|
||||
}
|
||||
_FreeUnmanagediOSNotificationDataArray(ptr, count);
|
||||
|
||||
return dataArray;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static IntPtr CsDictionaryToObjC(Dictionary<string, string> userInfo)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (userInfo == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
IntPtr dict = IntPtr.Zero;
|
||||
foreach (var item in userInfo)
|
||||
dict = _AddItemToNSDictionary(dict, item.Key, item.Value);
|
||||
return dict;
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static IntPtr CsAttachmentsToObjc(List<iOSNotificationAttachment> attachments)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (attachments == null || attachments.Count == 0)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var atts = IntPtr.Zero;
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
IntPtr error;
|
||||
atts = _AddAttachmentToNSArray(atts, attachment.Id, attachment.Url, out error);
|
||||
if (error != IntPtr.Zero)
|
||||
{
|
||||
if (atts != IntPtr.Zero)
|
||||
_ReleaseNSObject(atts);
|
||||
var msg = _NSErrorToMessage(error);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return atts;
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> NSDictionaryToCs(IntPtr dict)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
var ret = new Dictionary<string, string>();
|
||||
var handle = GCHandle.Alloc(ret);
|
||||
_ReadNSDictionary(GCHandle.ToIntPtr(handle), dict, ReceiveNSDictionaryKeyValue);
|
||||
handle.Free();
|
||||
return ret;
|
||||
#else
|
||||
return new Dictionary<string, string>();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static List<iOSNotificationAttachment> AttachmentsNSArrayToCs(IntPtr array)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (array == IntPtr.Zero)
|
||||
return null;
|
||||
var ret = new List<iOSNotificationAttachment>();
|
||||
var handle = GCHandle.Alloc(ret);
|
||||
_ReadAttachmentsNSArray(GCHandle.ToIntPtr(handle), array, ReceiveUNNotificationAttachment);
|
||||
handle.Free();
|
||||
return ret;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetApplicationBadge(int badge)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
_SetApplicationBadge(badge);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static int GetApplicationBadge()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _GetApplicationBadge();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool GetAppOpenedUsingNotification()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
return _GetAppOpenedUsingNotification();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static iOSNotificationWithUserInfo? GetLastNotificationData()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (_GetAppOpenedUsingNotification())
|
||||
{
|
||||
IntPtr ptr = _GetLastNotificationData();
|
||||
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
iOSNotificationWithUserInfo data;
|
||||
data.data = (iOSNotificationData)Marshal.PtrToStructure(ptr, typeof(iOSNotificationData));
|
||||
data.userInfo = NSDictionaryToCs(data.data.userInfo);
|
||||
data.data.userInfo = IntPtr.Zero;
|
||||
data.attachments = AttachmentsNSArrayToCs(data.data.attachments);
|
||||
data.data.attachments = IntPtr.Zero;
|
||||
_FreeUnmanagediOSNotificationDataArray(ptr, 1);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void SetNotificationCategories(IEnumerable<iOSNotificationCategory> categories)
|
||||
{
|
||||
var allActions = new Dictionary<string, IntPtr>();
|
||||
foreach (var category in categories)
|
||||
{
|
||||
foreach (var action in category.Actions)
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Id))
|
||||
throw new ArgumentException("Action must have a valid and unique ID");
|
||||
if (!allActions.ContainsKey(action.Id))
|
||||
allActions[action.Id] = action.CreateUNNotificationAction();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
IntPtr categorySet = IntPtr.Zero;
|
||||
foreach (var category in categories)
|
||||
{
|
||||
IntPtr actions = IntPtr.Zero;
|
||||
int count = category.Actions.Length;
|
||||
foreach (var action in category.Actions)
|
||||
actions = _AddActionToNSArray(actions, allActions[action.Id], count);
|
||||
IntPtr intentIdentifiers = IntPtr.Zero;
|
||||
count = category.IntentIdentifiers.Length;
|
||||
foreach (var idr in category.IntentIdentifiers)
|
||||
intentIdentifiers = _AddStringToNSArray(intentIdentifiers, idr, count);
|
||||
var cat = _CreateUNNotificationCategory(category.Id, category.HiddenPreviewsBodyPlaceholder, category.SummaryFormat, (int)category.Options,
|
||||
actions, intentIdentifiers);
|
||||
categorySet = _AddCategoryToCategorySet(categorySet, cat);
|
||||
}
|
||||
|
||||
_SetNotificationCategories(categorySet);
|
||||
|
||||
foreach (var act in allActions)
|
||||
_ReleaseNSObject(act.Value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 162
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 306facfaa63a94abea275dd2f15c37ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user