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 userInfo; internal List 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 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); [DllImport("__Internal")] internal static extern IntPtr _CreateUNTextInputNotificationAction(string id, string title, int options, 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)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)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 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 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 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 NSDictionaryToCs(IntPtr dict) { #if UNITY_IOS && !UNITY_EDITOR var ret = new Dictionary(); var handle = GCHandle.Alloc(ret); _ReadNSDictionary(GCHandle.ToIntPtr(handle), dict, ReceiveNSDictionaryKeyValue); handle.Free(); return ret; #else return new Dictionary(); #endif } public static List AttachmentsNSArrayToCs(IntPtr array) { #if UNITY_IOS && !UNITY_EDITOR if (array == IntPtr.Zero) return null; var ret = new List(); 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 categories) { var allActions = new Dictionary(); 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