fix:1、修复bug。2、更换sdk

This commit is contained in:
barry
2026-06-25 14:45:18 +08:00
parent 570f4635f2
commit 412180cbcb
223 changed files with 62891 additions and 46340 deletions
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Notifications.Tests")]
[assembly: InternalsVisibleTo("Unity.iOS.Notifications.Tests")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eaec56e278645461f8385f7af3c99e8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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 apps 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()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b313ac86fae05a14cb6f9c95468075e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c6a1338bb05c4940b3099839651a262
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
@@ -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:
@@ -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
@@ -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:
@@ -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
@@ -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:
@@ -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(&notificationData);
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(&notificationData, 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
@@ -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:
@@ -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
@@ -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:
@@ -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(&notificationData);
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
@@ -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:
@@ -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
@@ -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:
@@ -0,0 +1,16 @@
{
"name": "Unity.Notifications.iOS",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor",
"iOS"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1e8e55397bd004beaba78a667566665f
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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 apps 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 wont 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 apps 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 notifications 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 summarys 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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ab0873fd8f23f34681825b7642810ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7d19fa19fc06456eac4adb06cbf32e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6bc2d6d64e6740d78bacee53461b615
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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 categorys 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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f323c7093dfc41d587ecb64426dceb2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a54aa2e66430c45c2b076910262347d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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 apps notifications are displayed in Notification Center.
/// </summary>
public NotificationSetting NotificationCenterSetting
{
get { return (NotificationSetting)notificationCenterSetting; }
}
/// <summary>
/// The setting that indicates whether your apps notifications appear onscreen when the device is locked.
/// </summary>
public NotificationSetting LockScreenSetting
{
get { return (NotificationSetting)lockScreenSetting; }
}
/// <summary>
/// The setting that indicates whether your apps 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 apps 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; }
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f0fce3da9e80904c834cd6c21a9fc08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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 areas 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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56be22c2d38b781439f9da4ef07e2638
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 306facfaa63a94abea275dd2f15c37ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: