diff --git a/src/Firebase.Notifications/Android/Assets/DefaultIcon.png b/src/Firebase.Notifications/Android/Assets/DefaultIcon.png
new file mode 100644
index 0000000..23e2332
Binary files /dev/null and b/src/Firebase.Notifications/Android/Assets/DefaultIcon.png differ
diff --git a/src/Firebase.Notifications/Android/Impl.cpp.uxl b/src/Firebase.Notifications/Android/Impl.cpp.uxl
new file mode 100644
index 0000000..0f3046e
--- /dev/null
+++ b/src/Firebase.Notifications/Android/Impl.cpp.uxl
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Firebase.Notifications/Android/Impl.uno b/src/Firebase.Notifications/Android/Impl.uno
new file mode 100644
index 0000000..a681f4d
--- /dev/null
+++ b/src/Firebase.Notifications/Android/Impl.uno
@@ -0,0 +1,144 @@
+using Uno;
+using Uno.Graphics;
+using Uno.Collections;
+using Fuse;
+using Fuse.Controls;
+using Fuse.Triggers;
+using Fuse.Resources;
+
+using Fuse.Platform;
+using Uno.Compiler.ExportTargetInterop;
+using Uno.Compiler.ExportTargetInterop.Android;
+
+namespace Firebase.Notifications
+{
+ [ForeignInclude(Language.Java,
+ "java.io.IOException",
+ "android.app.Activity",
+ "android.content.Intent",
+ "android.os.AsyncTask",
+ "android.content.res.Resources",
+ "android.content.res.AssetManager",
+ "android.content.res.AssetFileDescriptor",
+ "android.os.Bundle",
+ "com.fuse.firebase.Notifications.PushNotificationReceiver",
+ "com.google.firebase.messaging.RemoteMessage")]
+ [Require("Gradle.Dependency.Compile", "com.google.firebase:firebase-messaging:12.0.1")]
+ extern(Android)
+ internal class AndroidImpl
+ {
+ public static event EventHandler RegistrationSucceeded;
+ public static event EventHandler RegistrationFailed;
+ public static event EventHandler> ReceivedNotification;
+
+ static bool _init = false;
+ static List _cachedMessages = new List();
+
+ internal static void Init()
+ {
+ if (!_init)
+ {
+ JInit();
+ _init = true;
+ Lifecycle.EnteringInteractive += OnEnteringInteractive;
+ Lifecycle.ExitedInteractive += OnExitedInteractive;
+ }
+ }
+
+ [Foreign(Language.Java)]
+ static void JInit()
+ @{
+ // Set up vars and hook into fuse intent listeners
+ com.fuse.Activity.subscribeToIntents(
+ new com.fuse.Activity.IntentListener() {
+ public void onIntent (Intent newIntent) {
+ String jsonStr = com.fuse.firebase.Notifications.PushNotificationReceiver.ToJsonString(newIntent);
+ if( jsonStr != "{}")
+ {
+ @{OnRecieve(string,bool):Call(jsonStr, false)};
+ }
+ }
+ },
+ "android.intent.action.MAIN");
+ String id = com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();
+ @{getRegistrationIdSuccess(string):Call(id)};
+ @}
+
+
+ [Foreign(Language.Java), ForeignFixedName]
+ static void RegistrationIDUpdated(string regid)
+ @{
+ @{getRegistrationIdSuccess(string):Call(regid)};
+ @}
+
+ static void getRegistrationIdSuccess(string regid)
+ {
+ var x = RegistrationSucceeded;
+ if (x!=null)
+ x(null, regid);
+ }
+
+ static void getRegistrationIdError(string message)
+ {
+ var x = RegistrationFailed;
+ if (x!=null)
+ x(null, message);
+ }
+
+ //----------------------------------------------------------------------
+
+ static void OnEnteringInteractive(ApplicationState newState)
+ {
+ NoteInteractivity(true);
+ var x = ReceivedNotification;
+ if (x!=null)
+ {
+ foreach (var message in _cachedMessages)
+ x(null, new KeyValuePair(message, true));
+ }
+ _cachedMessages.Clear();
+ }
+
+
+ static void OnExitedInteractive(ApplicationState newState)
+ {
+ NoteInteractivity(false);
+ }
+
+ //----------------------------------------------------------------------
+
+ [Foreign(Language.Java), ForeignFixedName]
+ static void OnRecieve2(string message, bool fromNotificationBar)
+ @{
+ @{OnRecieve(string,bool):Call(message, fromNotificationBar)};
+ @}
+
+ static void OnRecieve(string message, bool fromNotificationBar)
+ {
+ if (Lifecycle.State == ApplicationState.Interactive)
+ {
+ var x = ReceivedNotification;
+ if (x!=null)
+ x(null, new KeyValuePair(message, fromNotificationBar));
+ }
+ else
+ {
+ _cachedMessages.Add(message);
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ [Foreign(Language.Java)]
+ static void NoteInteractivity(bool isItInteractive)
+ @{
+ PushNotificationReceiver.InForeground = isItInteractive;
+ java.util.ArrayList maps = PushNotificationReceiver._cachedBundles;
+ if (isItInteractive && maps!=null && maps.size()>0) {
+ for (RemoteMessage remoteMessage : maps)
+ @{OnRecieve(string,bool):Call(PushNotificationReceiver.ToJsonString(remoteMessage), true)};
+ maps.clear();
+ }
+ @}
+ }
+}
diff --git a/src/Firebase.Notifications/Android/PushNotificationIDService.java b/src/Firebase.Notifications/Android/PushNotificationIDService.java
new file mode 100644
index 0000000..11363a4
--- /dev/null
+++ b/src/Firebase.Notifications/Android/PushNotificationIDService.java
@@ -0,0 +1,14 @@
+package com.fuse.firebase.Notifications;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+
+public class PushNotificationIDService extends FirebaseInstanceIdService
+{
+ @Override
+ public void onTokenRefresh()
+ {
+ String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+ com.foreign.Firebase.Notifications.AndroidImpl.RegistrationIDUpdated(refreshedToken);
+ }
+}
diff --git a/src/Firebase.Notifications/Android/PushNotificationReceiver.java b/src/Firebase.Notifications/Android/PushNotificationReceiver.java
new file mode 100644
index 0000000..6257fcb
--- /dev/null
+++ b/src/Firebase.Notifications/Android/PushNotificationReceiver.java
@@ -0,0 +1,282 @@
+package com.fuse.firebase.Notifications;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.support.v4.app.NotificationCompat;
+import android.content.res.AssetManager;
+import android.media.RingtoneManager;
+import android.app.Notification;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import org.json.JSONObject;
+import org.json.JSONException;
+
+public class PushNotificationReceiver extends FirebaseMessagingService
+{
+ public static ArrayList _cachedBundles = new ArrayList();
+ public static boolean InForeground = false;
+ public static String ACTION = "fuseFirebaseBackgroundNotify";
+ public static String EXTRA_KEY = "FirebaseRemoteNotification";
+ static int _notificationID = -1;
+ public static int nextID() { return _notificationID += 1; }
+ private static Object lock = new Object();
+ private static final Set GOOGLE_KEYS =
+ new HashSet(Arrays.asList(new String[] {"google.collapse_key",
+ "google.from",
+ "google.message_id",
+ "google.message_type",
+ "google.sent_time",
+ "google.to",
+ "google.ttl",
+ "collapse_key"}));
+
+ public static String ToJsonString(Intent newIntent)
+ {
+ Bundle map = newIntent.getExtras();
+ JSONObject jmessage = new JSONObject();
+ JSONObject data = new JSONObject();
+ if (newIntent.getExtras() != null) {
+ for (String key : newIntent.getExtras().keySet())
+ {
+ if (GOOGLE_KEYS.contains(key))
+ {
+ try { jmessage.putOpt(key, map.getString(key)); } catch (Exception e) {}
+ }
+ else
+ {
+ try
+ {
+ data.put(key, new JSONObject(map.getString(key)));
+ }
+ catch (Exception e)
+ {
+ String val = map.getString(key);
+ try { data.put(key, val); } catch (JSONException e1) {}
+ }
+ }
+ }
+ if (data.length() > 0)
+ {
+ try { jmessage.putOpt("data", data); } catch (Exception e) {}
+ }
+ }
+ return jmessage.toString();
+ }
+
+ public static String ToJsonString(RemoteMessage message)
+ {
+ Map map = message.getData();
+ JSONObject jmessage = new JSONObject();
+ try { jmessage.putOpt("google.collapse_key", message.getCollapseKey()); } catch (Exception e) {}
+ try { jmessage.putOpt("google.from", message.getFrom()); } catch (Exception e) {}
+ try { jmessage.putOpt("google.message_id", message.getMessageId()); } catch (Exception e) {}
+ try { jmessage.putOpt("google.message_type", message.getMessageType()); } catch (Exception e) {}
+ try { jmessage.putOpt("google.sent_time", Long.toString(message.getSentTime())); } catch (Exception e) {}
+ try { jmessage.putOpt("google.to", message.getTo()); } catch (Exception e) {}
+ try { jmessage.putOpt("google.ttl", Long.toString(message.getTtl())); } catch (Exception e) {}
+
+ // extract data
+ JSONObject data = new JSONObject();
+ for (String key : map.keySet())
+ {
+ try
+ {
+ data.put(key, new JSONObject(map.get(key)));
+ }
+ catch (Exception e)
+ {
+ String val = map.get(key);
+ try
+ {
+ data.put(key, val);
+ }
+ catch (JSONException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ }
+ if (map.keySet().size()>0)
+ {
+ try {
+ jmessage.put("data", data);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // extract notification
+ RemoteMessage.Notification notif = message.getNotification();
+ if (notif!=null)
+ {
+ JSONObject jnotif = new JSONObject();
+ try { jnotif.putOpt("title", notif.getTitle()); } catch (Exception e) {}
+ try { jnotif.putOpt("body", notif.getBody()); } catch (Exception e) {}
+ try { jnotif.putOpt("icon", notif.getIcon()); } catch (Exception e) {}
+ try { jnotif.putOpt("sound", notif.getSound()); } catch (Exception e) {}
+ try { jnotif.putOpt("tag", notif.getTag()); } catch (Exception e) {}
+ try { jnotif.putOpt("color", notif.getColor()); } catch (Exception e) {}
+ try { jnotif.putOpt("click_action", notif.getClickAction()); } catch (Exception e) {}
+ try { jnotif.putOpt("body_loc_key", notif.getBodyLocalizationKey()); } catch (Exception e) {}
+ try { jnotif.putOpt("body_loc_args", notif.getBodyLocalizationArgs()); } catch (Exception e) {}
+ try { jnotif.putOpt("title_loc_key", notif.getTitleLocalizationKey()); } catch (Exception e) {}
+ try { jnotif.putOpt("title_loc_args", notif.getTitleLocalizationArgs()); } catch (Exception e) {}
+ try {
+ jmessage.put("notification", jnotif);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ return jmessage.toString();
+ }
+
+ static void cacheBundle(RemoteMessage message)
+ {
+ PushNotificationReceiver._cachedBundles.add(message);
+ }
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage)
+ {
+ synchronized (lock)
+ {
+ DoIt(remoteMessage);
+ }
+ }
+
+ // OnNotificationRecieved(this, remoteMessage.getFrom(), data);
+ // static void OnNotificationRecieved(Java.Object listener, String from, String data)
+ public void DoIt(RemoteMessage remoteMessage)
+ {
+ Map map = remoteMessage.getData();
+ if (!PushNotificationReceiver.InForeground)
+ {
+ if (map.containsKey("title") || map.containsKey("body"))
+ {
+ SpitOutNotification(remoteMessage);
+ }
+ else
+ {
+ cacheBundle(remoteMessage);
+ }
+ }
+ else
+ {
+ com.foreign.Firebase.Notifications.AndroidImpl.OnRecieve2(ToJsonString(remoteMessage), false);
+ }
+ }
+
+ void SpitOutNotification(RemoteMessage remoteMessage)
+ {
+ Context context = this;
+ int id = PushNotificationReceiver.nextID();
+ Map map = remoteMessage.getData();
+ String title = map.get("title");
+ String body = map.get("body");
+ String sound = map.get("sound");
+ String icon = map.get("icon");
+
+ String jsonStr = ToJsonString(remoteMessage);
+ Intent intent = new Intent(context, @(Activity.Package).@(Activity.Name).class);
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.setAction(PushNotificationReceiver.ACTION);
+ intent.putExtra(PushNotificationReceiver.EXTRA_KEY, jsonStr);
+
+ android.app.PendingIntent pendingIntent =
+ android.app.PendingIntent.getActivity(context, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
+
+ NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+
+ String channel_name = "my_package_channel";
+ String channel_id = "my_package_channel_1";
+ String channel_description = "my_package_first_channel";
+
+ int importance = NotificationManager.IMPORTANCE_HIGH;
+ NotificationCompat.Builder notificationBuilder = null;
+ if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
+ {
+ NotificationChannel mChannel = notificationManager.getNotificationChannel(channel_id);
+ if (mChannel == null)
+ {
+ mChannel = new NotificationChannel(channel_id, channel_name, importance);
+ notificationManager.createNotificationChannel(mChannel);
+ }
+ // Android > 8 support for Channels because it not supported on previous versions
+ notificationBuilder = new NotificationCompat.Builder(this, channel_id);
+ }
+ else{
+ // Android < 7 Notification Builder init
+ notificationBuilder = new NotificationCompat.Builder(this);
+ }
+
+ notificationBuilder = new NotificationCompat.Builder(context)
+ .setSmallIcon(@(Activity.Package).R.mipmap.notif)
+ .setContentTitle(title)
+ .setContentText(body)
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent);
+
+ if (sound=="default")
+ {
+ android.net.Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ notificationBuilder.setSound(defaultSoundUri);
+ }
+
+ if (icon != null && !icon.isEmpty())
+ {
+ int iconResourceID = com.fuse.R.get(icon);
+ if (iconResourceID!=-1)
+ {
+ android.graphics.Bitmap bm = android.graphics.BitmapFactory.decodeResource(context.getResources(), iconResourceID);
+ notificationBuilder .setLargeIcon(bm);
+ }
+ else
+ {
+ String packageName = "@(Project.Name)";
+ java.io.InputStream afs = com.fuse.firebase.BundleFiles.OpenBundledFile(context, packageName, icon);
+ if (afs!=null)
+ {
+ android.graphics.Bitmap bm = android.graphics.BitmapFactory.decodeStream(afs);
+ try
+ {
+ afs.close();
+ }
+ catch (IOException e)
+ {
+ Log.d("@(Project.Name)", "Could close the notification icon '" + icon);
+ e.printStackTrace();
+ return;
+ }
+ notificationBuilder .setLargeIcon(bm);
+ }
+ else
+ {
+ Log.d("@(Project.Name)", "Could not the load icon '" + icon + "' as either a bundled file or android resource");
+ }
+ }
+ }
+
+ Notification n = notificationBuilder .build();
+ if (sound!="")
+ n.defaults |= Notification.DEFAULT_SOUND;
+ n.defaults |= Notification.DEFAULT_LIGHTS;
+ n.defaults |= Notification.DEFAULT_VIBRATE;
+
+ notificationManager.notify(id, n);
+ }
+}
diff --git a/src/Firebase.Notifications/Common.uno b/src/Firebase.Notifications/Common.uno
new file mode 100644
index 0000000..622e672
--- /dev/null
+++ b/src/Firebase.Notifications/Common.uno
@@ -0,0 +1,169 @@
+using Uno.Compiler.ExportTargetInterop;
+using Uno;
+using Uno.Graphics;
+using Uno.Platform;
+using Uno.Collections;
+using Fuse;
+using Fuse.Controls;
+using Uno.Threading;
+
+namespace Firebase.Notifications
+{
+ [Require("Entity","Firebase.Core.Init()")]
+ [ForeignInclude(Language.Java,
+ "android.util.Log",
+ "com.google.firebase.iid.FirebaseInstanceId")]
+ [extern(iOS) Require("Source.Include", "Firebase/Firebase.h")]
+ public static class NotificationService
+ {
+ extern(Android)
+ static NotificationService()
+ {
+ Firebase.Core.Init();
+ AndroidImpl.ReceivedNotification += OnReceived;
+ AndroidImpl.RegistrationFailed += OnRegistrationFailed;
+ AndroidImpl.RegistrationSucceeded += OnRegistrationSucceeded;
+ AndroidImpl.Init();
+ }
+
+ extern(iOS)
+ static NotificationService()
+ {
+ Firebase.Core.Init();
+ iOSImpl.ReceivedNotification += OnReceived;
+ iOSImpl.NotificationRegistrationFailed += OnRegistrationFailed;
+ iOSImpl.NotificationRegistrationSucceeded += OnRegistrationSucceeded;
+ iOSImpl.Init();
+ }
+
+ public static void OnReceived(object sender, KeyValuePair notification)
+ {
+ var x = _receivedNotification;
+ if (x!=null){
+ x(null, notification);
+ }
+ else
+ _pendingNotifications.Add(notification);
+ }
+
+ public static void OnRegistrationFailed(object sender, string message)
+ {
+ var x = _registrationFailed;
+ if (x!=null)
+ x(null, message);
+ else
+ {
+ _pendingSuccess = null;
+ _pendingFailure = message;
+ }
+ }
+
+ public static void OnRegistrationSucceeded(object sender, string message)
+ {
+ var x = _registrationSucceeded;
+ if (x!=null)
+ x(null, message);
+ else
+ {
+ _pendingFailure = null;
+ _pendingSuccess = message;
+ }
+ }
+
+ static event EventHandler _registrationSucceeded;
+ static event EventHandler _registrationFailed;
+ static event EventHandler> _receivedNotification;
+ static string _pendingSuccess;
+ static string _pendingFailure;
+ static List> _pendingNotifications = new List>();
+
+ internal static event EventHandler> ReceivedNotification
+ {
+ add
+ {
+ _receivedNotification += value;
+ foreach (var n in _pendingNotifications)
+ {
+ debug_log "_pendingNotifications : "+n.Key+" fromNotificationBar:"+n.Value;
+ value(null, n);
+ }
+ _pendingNotifications.Clear();
+ }
+ remove {
+ _receivedNotification -= value;
+ }
+ }
+
+ // NOTE: We dont clean the _pendingSuccess or _pendingFailure fields
+ // As each consumer of PushNotifications will need to know these details.
+
+ internal static event EventHandler RegistrationSucceeded
+ {
+ add
+ {
+ _registrationSucceeded += value;
+ if (_pendingSuccess!=null)
+ value(null, _pendingSuccess);
+ }
+ remove {
+ _registrationSucceeded -= value;
+ }
+ }
+
+ internal static event EventHandler RegistrationFailed
+ {
+ add
+ {
+ _registrationFailed += value;
+ if (_pendingFailure!=null)
+ value(null, _pendingFailure);
+ }
+ remove {
+ _registrationFailed -= value;
+ }
+ }
+
+ [Foreign(Language.ObjC)]
+ public extern(iOS) static void ClearBadgeNumber()
+ @{
+ [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
+ @}
+
+ public extern(!iOS) static void ClearBadgeNumber() { }
+
+ [Foreign(Language.ObjC)]
+ public extern(iOS) static void ClearAllNotifications()
+ @{
+ [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
+ @}
+
+ [Foreign(Language.Java)]
+ public extern(Android) static void ClearAllNotifications()
+ @{
+ android.app.Activity activity = com.fuse.Activity.getRootActivity();
+ android.app.NotificationManager nMgr = (android.app.NotificationManager)activity.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
+ nMgr.cancelAll();
+ @}
+
+ public extern(!iOS && !Android) static void ClearAllNotifications() { }
+
+ [Foreign(Language.ObjC)]
+ public extern(iOS) static String GetFCMToken()
+ @{
+ NSString *fcmToken = [[FIRInstanceID instanceID] token];
+ return fcmToken;
+ @}
+
+ [Foreign(Language.Java)]
+ public extern(Android) static String GetFCMToken()
+ @{
+ String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+ Log.d("TOKEN", "Refreshed token: " + refreshedToken);
+ return refreshedToken;
+ @}
+
+ public extern(!iOS && !Android) static String GetFCMToken() { return ""; }
+
+
+ }
+}
diff --git a/src/Firebase.Notifications/Firebase.Notifications.unoproj b/src/Firebase.Notifications/Firebase.Notifications.unoproj
new file mode 100644
index 0000000..49f8aac
--- /dev/null
+++ b/src/Firebase.Notifications/Firebase.Notifications.unoproj
@@ -0,0 +1,37 @@
+{
+ "Copyright": "Copyright (c) Fusetools AS 2018",
+ "Description": "Support for Firebase Notifications On Mobile",
+ "Publisher": "Fusetools AS",
+ "Version": "1.0.0-rc2",
+ "Package": {
+ "ProjectUrl": "https://fusetools.com"
+ },
+ "Packages": [
+ "Uno.Collections",
+ "Uno.Threading",
+ "Fuse.Controls",
+ "Fuse.Elements",
+ "Fuse.Reactive",
+ "Fuse.Scripting",
+ "Fuse.Common",
+ "Fuse.Platform"
+ ],
+ "Projects": [
+ "../Firebase/Firebase.unoproj"
+ ],
+ "Includes": [
+ "Common.uno:Source",
+ "JS.uno:Source",
+ "Android/Impl.uno:Source",
+ "Android/Assets/DefaultIcon.png:File",
+ "Android/Impl.cpp.uxl:Extensions",
+ "Android/PushNotificationReceiver.java:Java:android",
+ "Android/PushNotificationIDService.java:Java:android",
+ "iOS/iOSFirebaseNotificationCallbacks.h:ObjCHeader:iOS",
+ "iOS/iOSFirebaseNotificationCallbacks.mm:ObjCSource:iOS",
+ "iOS/AppDelegateFirebaseNotify.h:ObjCHeader:iOS",
+ "iOS/AppDelegateFirebaseNotify.mm:ObjCSource:iOS",
+ "iOS/iOSImpl.uno:Source",
+ "iOS/Impl.uxl:Extensions"
+ ]
+}
diff --git a/src/Firebase.Notifications/JS.uno b/src/Firebase.Notifications/JS.uno
new file mode 100644
index 0000000..0fbfe3c
--- /dev/null
+++ b/src/Firebase.Notifications/JS.uno
@@ -0,0 +1,146 @@
+using Uno;
+using Uno.UX;
+using Uno.Platform;
+using Uno.Collections;
+using Uno.Compiler.ExportTargetInterop;
+using Fuse;
+using Fuse.Scripting;
+using Fuse.Reactive;
+
+namespace Firebase.Notifications
+{
+ /**
+ @scriptmodule Firebase/Notifications
+
+ Handles push notification from Firebase
+
+ @include Docs/Guide.md
+
+ ## Remarks
+
+ This module is an @EventEmitter, so the methods from @EventEmitter can be used to listen to events.
+
+ You need to add a reference to `Firebase.Notifications` in your project file to use this feature.
+ */
+ [UXGlobalModule]
+ public sealed class NotificationModule : NativeEventEmitterModule
+ {
+ static readonly NotificationModule _instance;
+ extern(iOS) readonly iOSImpl _iOSImpl;
+ static NativeEvent _onRegistrationSucceedediOS;
+
+ static NativeEvent onReceivedMessage;
+ static NativeEvent onRegistrationFailed;
+ static NativeEvent onRegistrationSucceeded;
+
+ public NotificationModule()
+ : base(true,
+ "receivedMessage",
+ "registrationSucceeded")
+ {
+ if (_instance != null) return;
+ Resource.SetGlobalKey(_instance = this, "Firebase/Notifications");
+
+ if defined(iOS)
+ _iOSImpl = new iOSImpl();
+
+ onReceivedMessage = new NativeEvent("onReceivedMessage");
+ onRegistrationFailed = new NativeEvent("onRegistrationFailed");
+ onRegistrationSucceeded = new NativeEvent("onRegistrationSucceeded");
+
+ // Old-style events for backwards compatibility
+ On("receivedMessage", onReceivedMessage);
+ // Note: If we decide to remove these old-style events in the future, the
+ // "error" event will no longer have a listener by default, meaning that the
+ // module will then throw an exception on "error" (as per the way
+ // EventEmitter works), unlike the current behaviour. To retain the current
+ // behaviour we might then want to add a dummy listener to the "error"
+ // event.
+ On("error", onRegistrationFailed);
+ On("registrationSucceeded", onRegistrationSucceeded);
+
+ AddMember(onReceivedMessage);
+ AddMember(onRegistrationSucceeded);
+ AddMember(onRegistrationFailed);
+ AddMember(new NativeFunction("clearBadgeNumber", ClearBadgeNumber));
+ AddMember(new NativeFunction("clearAllNotifications", ClearAllNotifications));
+ AddMember(new NativeFunction("getFCMToken", GetFCMToken));
+ _onRegistrationSucceedediOS = new NativeEvent("onRegistrationSucceedediOS");
+ AddMember(_onRegistrationSucceedediOS);
+
+ Firebase.Notifications.NotificationService.ReceivedNotification += OnReceivedNotification;
+ Firebase.Notifications.NotificationService.RegistrationSucceeded += OnRegistrationSucceeded;
+ Firebase.Notifications.NotificationService.RegistrationFailed += OnRegistrationFailed;
+ }
+
+ /**
+ @scriptevent receivedMessage
+ @param message The content of the notification as json
+
+ Triggered when your app receives a notification.
+ */
+ void OnReceivedNotification(object sender, KeyValuePairmessage)
+ {
+ Emit("receivedMessage", message.Key, message.Value);
+ }
+
+ /**
+ @scriptevent registrationSucceeded
+ @param message The registration key from the backend
+
+ Triggered when your app registers with the APNS or GCM backend.
+ */
+ void OnRegistrationSucceeded(object sender, string message)
+ {
+ Emit("registrationSucceeded", message);
+ }
+
+ static void OnRegistrationSucceedediOS(string message) {
+ _onRegistrationSucceedediOS.RaiseAsync(message);
+ }
+
+ /**
+ @scriptevent error
+ @param message A backend specific reason for the failure.
+
+ Called if your app fails to register with the backend.
+ */
+ void OnRegistrationFailed(object sender, string message)
+ {
+ EmitError(message);
+ }
+
+ /**
+ @scriptmethod clearBadgeNumber
+
+ Clears the badge number shown on the iOS home screen.
+
+ Has no effects on other platforms.
+ */
+ public object ClearBadgeNumber(Context context, object[] args)
+ {
+ Firebase.Notifications.NotificationService.ClearBadgeNumber();
+ return null;
+ }
+
+ /**
+ @scriptmethod clearAllNotifications
+
+ Cancels all previously shown notifications.
+ */
+ public object ClearAllNotifications(Context context, object[] args)
+ {
+ Firebase.Notifications.NotificationService.ClearAllNotifications();
+ return null;
+ }
+
+ public object GetFCMToken(Context context, object[] args)
+ {
+ var token = Firebase.Notifications.NotificationService.GetFCMToken();
+ if (token != null) {
+ Emit("registrationSucceeded", token);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.h b/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.h
new file mode 100644
index 0000000..51497cc
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#ifdef __OBJC__
+#include
+#include
+#include
+
+@interface uContext (FirebaseNotify)
+- (void)application:(UIApplication *)application initializeFirebaseNotifications:(NSDictionary *)launchOptions;
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
+- (void)application:(UIApplication *)application dispatchPushNotification:(NSDictionary *)userInfo fromBar:(BOOL)fromBar;
+@end
+
+#endif
diff --git a/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.mm b/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.mm
new file mode 100644
index 0000000..dfec23c
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/AppDelegateFirebaseNotify.mm
@@ -0,0 +1,36 @@
+#include
+#include
+#include "AppDelegateFirebaseNotify.h"
+@{Fuse.Platform.Lifecycle:IncludeDirective}
+@{Firebase.Notifications.iOSImpl:IncludeDirective}
+
+@implementation uContext (FirebaseNotify)
+
+- (void)application:(UIApplication *)application initializeFirebaseNotifications:(NSDictionary *)launchOptions {
+ if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
+ [self application:application dispatchPushNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] fromBar:YES];
+ }
+#if (!@(Project.iOS.PushNotifications.RegisterOnLaunch:IsSet)) || @(Project.iOS.PushNotifications.RegisterOnLaunch:Or(0))
+ @{Firebase.Notifications.iOSImpl.RegisterForPushNotifications():Call()};
+#endif
+}
+
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
+ uAutoReleasePool pool;
+ @{Fuse.Platform.ApplicationState} state = @{Fuse.Platform.Lifecycle.State:Get()};
+ bool fromNotifBar = application.applicationState != UIApplicationStateActive;
+ [self application:application dispatchPushNotification:userInfo fromBar:fromNotifBar];
+}
+
+- (void)application:(UIApplication *)application dispatchPushNotification:(NSDictionary *)userInfo fromBar:(BOOL)fromBar {
+ uAutoReleasePool pool;
+ NSError* err = NULL;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:userInfo options:0 error:&err];
+ if (jsonData)
+ {
+ NSString* nsJsonPayload = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ @{Firebase.Notifications.iOSImpl.OnReceivedNotification(string, bool):Call(nsJsonPayload, fromBar)};
+ }
+}
+
+@end
diff --git a/src/Firebase.Notifications/iOS/Impl.uxl b/src/Firebase.Notifications/iOS/Impl.uxl
new file mode 100644
index 0000000..876c504
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/Impl.uxl
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.h b/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.h
new file mode 100644
index 0000000..659ea5f
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.h
@@ -0,0 +1,5 @@
+#import
+#include
+
+@interface FireNotificationCallbacks : NSObject
+@end
diff --git a/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.mm b/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.mm
new file mode 100644
index 0000000..2523423
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/iOSFirebaseNotificationCallbacks.mm
@@ -0,0 +1,17 @@
+#import
+#include <@{Firebase.Notifications.NotificationModule:Include}>
+#include <@{ObjC.Object:Include}>
+#include
+@{Firebase.Notifications.iOSImpl:IncludeDirective}
+
+@implementation FireNotificationCallbacks : NSObject
+
+
+- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
+ @{Firebase.Notifications.iOSImpl.OnNotificationRegistrationSucceeded(string):Call(fcmToken)};
+}
+- (void)messaging:(nonnull FIRMessaging *)messaging
+ didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage{
+}
+
+@end
diff --git a/src/Firebase.Notifications/iOS/iOSImpl.uno b/src/Firebase.Notifications/iOS/iOSImpl.uno
new file mode 100644
index 0000000..fbfc771
--- /dev/null
+++ b/src/Firebase.Notifications/iOS/iOSImpl.uno
@@ -0,0 +1,150 @@
+using Uno;
+using Uno.UX;
+using Uno.Collections;
+using Uno.Compiler.ExportTargetInterop;
+using Fuse;
+using Fuse.Triggers;
+using Uno.Threading;
+using Uno.Graphics;
+using Fuse.Platform;
+
+namespace Firebase.Notifications
+{
+ [Require("Cocoapods.Podfile.Target", "pod 'Firebase/Messaging'")]
+ [extern(iOS) Require("Source.Include", "iOSFirebaseNotificationCallbacks.h")]
+ [extern(iOS) Require("Source.Include", "Firebase/Firebase.h")]
+ [Require("uContext.SourceFile.DidFinishLaunching", "[self application:[notification object] initializeFirebaseNotifications:[notification userInfo]];")]
+ [Require("uContext.SourceFile.Declaration", "#include ")]
+
+ [Require("Entity", "Firebase.Notifications.iOSImpl.OnNotificationRegistrationSucceeded(string)")]
+ [Require("Entity", "Firebase.Notifications.iOSImpl.OnNotificationRegistrationFailed(string)")]
+ [Require("Entity", "Firebase.Notifications.iOSImpl.OnReceivedNotification(string,bool)")]
+
+ extern(iOS)
+ public class iOSImpl
+ {
+
+ public static event EventHandler> ReceivedNotification;
+ public static event EventHandler NotificationRegistrationFailed;
+ public static event EventHandler NotificationRegistrationSucceeded;
+ static List> DelayedNotifications = new List>();
+
+ static bool _init = false;
+ extern(ios) static internal ObjC.Object _iosDelegate;
+
+ public static void Init()
+ {
+ if (!_init)
+ {
+ if defined(iOS)
+ {
+ iOSInit();
+ _init = true;
+ }
+ }
+ }
+
+ [Foreign(Language.ObjC)]
+ extern(iOS)
+ public static void iOSInit()
+ @{
+ FireNotificationCallbacks* fireCB = [[FireNotificationCallbacks alloc] init];
+ @{_iosDelegate:Set(fireCB)};
+ [FIRMessaging messaging].delegate = (id)fireCB;
+ @}
+
+ internal static void OnReceivedNotification(string notification, bool fromNotificationBar)
+ {
+ if (Lifecycle.State == ApplicationState.Foreground ||
+ Lifecycle.State == ApplicationState.Interactive)
+ {
+ var handler = ReceivedNotification;
+ if (handler != null)
+ handler(null, new KeyValuePair(notification, fromNotificationBar));
+ }
+ else
+ {
+ DelayedNotifications.Add(new KeyValuePair(notification, fromNotificationBar));
+ Lifecycle.EnteringForeground += DispatchDelayedNotifications;
+ }
+ }
+ private static void DispatchDelayedNotifications(ApplicationState state)
+ {
+ var handler = ReceivedNotification;
+ if (handler != null)
+ foreach (var n in DelayedNotifications)
+ {
+ handler(null, n);
+ }
+ DelayedNotifications.Clear();
+ Lifecycle.EnteringForeground -= DispatchDelayedNotifications;
+ }
+
+ static string DelayedReason = "";
+ internal static void OnNotificationRegistrationFailed(string reason)
+ {
+ if (Lifecycle.State == ApplicationState.Foreground ||
+ Lifecycle.State == ApplicationState.Interactive)
+ {
+ EventHandler handler = NotificationRegistrationFailed;
+ if (handler != null)
+ handler(null, reason);
+ }
+ else
+ {
+ DelayedReason = reason;
+ Lifecycle.EnteringForeground += DispatchDelayedReason;
+ }
+ }
+ private static void DispatchDelayedReason(ApplicationState state)
+ {
+ EventHandler handler = NotificationRegistrationFailed;
+ if (handler != null)
+ handler(null, DelayedReason);
+ DelayedReason = "";
+ Lifecycle.EnteringForeground -= DispatchDelayedReason;
+ }
+
+ static string DelayedRegToken = "";
+ internal static void OnNotificationRegistrationSucceeded(string regID)
+ {
+ if (Lifecycle.State == ApplicationState.Foreground ||
+ Lifecycle.State == ApplicationState.Interactive)
+ {
+ EventHandler handler = NotificationRegistrationSucceeded;
+ if (handler != null)
+ handler(null, regID);
+ }
+ else
+ {
+ DelayedRegToken = regID;
+ Lifecycle.EnteringForeground += DispatchDelayedRegToken;
+ }
+ }
+ private static void DispatchDelayedRegToken(ApplicationState state)
+ {
+ EventHandler handler = NotificationRegistrationSucceeded;
+ if (handler != null)
+ handler(null, DelayedRegToken);
+ DelayedRegToken = "";
+ Lifecycle.EnteringForeground -= DispatchDelayedRegToken;
+ }
+
+ [Foreign(Language.ObjC)]
+ internal static void RegisterForPushNotifications()
+ @{
+ UIApplication* application = [UIApplication sharedApplication];
+ if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
+ // use registerUserNotificationSettings
+ [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
+ [application registerForRemoteNotifications];
+ } else {
+ // use registerForRemoteNotificationTypes:
+ [application registerForRemoteNotificationTypes:
+ UIRemoteNotificationTypeBadge |
+ UIRemoteNotificationTypeSound |
+ UIRemoteNotificationTypeAlert];
+ }
+ @}
+ }
+}