488 lines
19 KiB
C#
488 lines
19 KiB
C#
![]() |
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
|
|||
|
/// </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>
|
|||
|
/// The type of sound to use for the notification.
|
|||
|
/// See Apple documentation for details.
|
|||
|
/// </summary>
|
|||
|
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationsound?language=objc"/>
|
|||
|
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,
|
|||
|
}
|
|||
|
|
|||
|
[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 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.
|
|||
|
/// </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.
|
|||
|
/// </summary>
|
|||
|
/// <see href="https://developer.apple.com/documentation/usernotifications/unnotificationsound/2963118-defaultcriticalsoundwithaudiovol?language=objc"/>
|
|||
|
public float? SoundVolume { get; set; }
|
|||
|
|
|||
|
/// <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.
|
|||
|
/// </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.
|
|||
|
/// </summary>
|
|||
|
/// <see cref="https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649857-attachments?language=objc"/>
|
|||
|
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";
|
|||
|
if (!trigger.UtcTime)
|
|||
|
trigger = trigger.ToUtc();
|
|||
|
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 = true,
|
|||
|
Repeats = data.trigger.calendar.repeats != 0
|
|||
|
};
|
|||
|
if (userInfo != null)
|
|||
|
{
|
|||
|
string utc;
|
|||
|
if (userInfo.TryGetValue("OriginalUtc", out utc))
|
|||
|
{
|
|||
|
if (utc == "0")
|
|||
|
trigger = trigger.ToLocal();
|
|||
|
}
|
|||
|
else
|
|||
|
trigger.UtcTime = false;
|
|||
|
}
|
|||
|
else
|
|||
|
trigger.UtcTime = false;
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|