summaryrefslogtreecommitdiff
path: root/StoneIsland/plugins/cordova-plugin-firebasex/src
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-firebasex/src')
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePlugin.java2441
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiver.java28
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiverManager.java41
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessagingService.java348
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/JavaScriptException.java45
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/OnNotificationOpenReceiver.java36
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/build.gradle48
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/colors.xml3
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/android/cordova-plugin-firebase-strings.xml5
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.h11
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.m557
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.h119
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.m1757
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.h5
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.m17
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.h6
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.m24
-rw-r--r--StoneIsland/plugins/cordova-plugin-firebasex/src/ios/GoogleService-Info.plist6
18 files changed, 5497 insertions, 0 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePlugin.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePlugin.java
new file mode 100644
index 00000000..d81419e9
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePlugin.java
@@ -0,0 +1,2441 @@
+package org.apache.cordova.firebase;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.app.NotificationChannel;
+import android.content.ContentResolver;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.media.AudioAttributes;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import android.util.Base64;
+import android.util.Log;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
+
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignIn;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInClient;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.common.api.CommonStatusCodes;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.analytics.FirebaseAnalytics;
+import com.google.firebase.auth.AuthCredential;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.GetTokenResult;
+import com.google.firebase.auth.GoogleAuthProvider;
+import com.google.firebase.auth.OAuthProvider;
+import com.google.firebase.auth.UserProfileChangeRequest;
+import com.google.firebase.firestore.CollectionReference;
+import com.google.firebase.firestore.DocumentReference;
+import com.google.firebase.firestore.DocumentSnapshot;
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.Query;
+import com.google.firebase.firestore.QueryDocumentSnapshot;
+import com.google.firebase.firestore.QuerySnapshot;
+import com.google.firebase.firestore.Query.Direction;
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
+import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo;
+import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
+import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
+import com.google.firebase.perf.FirebasePerformance;
+import com.google.firebase.perf.metrics.Trace;
+
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.List;
+
+// Firebase PhoneAuth
+import java.util.concurrent.TimeUnit;
+
+import com.google.firebase.FirebaseException;
+import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
+import com.google.firebase.FirebaseTooManyRequestsException;
+import com.google.firebase.auth.PhoneAuthCredential;
+import com.google.firebase.auth.PhoneAuthProvider;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import static android.content.Context.MODE_PRIVATE;
+
+public class FirebasePlugin extends CordovaPlugin {
+
+ protected static FirebasePlugin instance = null;
+ private FirebaseAnalytics mFirebaseAnalytics;
+ private FirebaseCrashlytics firebaseCrashlytics;
+ private FirebaseFirestore firestore;
+ private Gson gson;
+ private FirebaseAuth.AuthStateListener authStateListener;
+ private boolean authStateChangeListenerInitialized = false;
+ private static CordovaInterface cordovaInterface = null;
+ protected static Context applicationContext = null;
+ private static Activity cordovaActivity = null;
+
+ protected static final String TAG = "FirebasePlugin";
+ protected static final String JS_GLOBAL_NAMESPACE = "FirebasePlugin.";
+ protected static final String KEY = "badge";
+ protected static final int GOOGLE_SIGN_IN = 0x1;
+ protected static final String SETTINGS_NAME = "settings";
+ private static final String CRASHLYTICS_COLLECTION_ENABLED = "firebase_crashlytics_collection_enabled";
+ private static final String ANALYTICS_COLLECTION_ENABLED = "firebase_analytics_collection_enabled";
+ private static final String PERFORMANCE_COLLECTION_ENABLED = "firebase_performance_collection_enabled";
+
+ private static boolean inBackground = true;
+ private static ArrayList<Bundle> notificationStack = null;
+ private static CallbackContext notificationCallbackContext;
+ private static CallbackContext tokenRefreshCallbackContext;
+ private static CallbackContext activityResultCallbackContext;
+ private static CallbackContext authResultCallbackContext;
+
+ private static NotificationChannel defaultNotificationChannel = null;
+ public static String defaultChannelId = null;
+ public static String defaultChannelName = null;
+
+ private Map<String, AuthCredential> authCredentials = new HashMap<String, AuthCredential>();
+ private Map<String, OAuthProvider> authProviders = new HashMap<String, OAuthProvider>();
+
+ @Override
+ protected void pluginInitialize() {
+ instance = this;
+ cordovaActivity = this.cordova.getActivity();
+ applicationContext = cordovaActivity.getApplicationContext();
+ final Bundle extras = cordovaActivity.getIntent().getExtras();
+ FirebasePlugin.cordovaInterface = this.cordova;
+ firebaseCrashlytics = FirebaseCrashlytics.getInstance();
+ this.cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ Log.d(TAG, "Starting Firebase plugin");
+
+ if(getMetaDataFromManifest(CRASHLYTICS_COLLECTION_ENABLED)){
+ setPreference(CRASHLYTICS_COLLECTION_ENABLED, true);
+ }
+
+ if(getMetaDataFromManifest(ANALYTICS_COLLECTION_ENABLED)){
+ setPreference(ANALYTICS_COLLECTION_ENABLED, true);
+ }
+
+ if(getMetaDataFromManifest(PERFORMANCE_COLLECTION_ENABLED)){
+ setPreference(PERFORMANCE_COLLECTION_ENABLED, true);
+ }
+
+ FirebaseApp.initializeApp(applicationContext);
+ mFirebaseAnalytics = FirebaseAnalytics.getInstance(applicationContext);
+
+ authStateListener = new AuthStateListener();
+ FirebaseAuth.getInstance().addAuthStateListener(authStateListener);
+
+ firestore = FirebaseFirestore.getInstance();
+ gson = new Gson();
+
+ if (extras != null && extras.size() > 1) {
+ if (FirebasePlugin.notificationStack == null) {
+ FirebasePlugin.notificationStack = new ArrayList<Bundle>();
+ }
+ if (extras.containsKey("google.message_id")) {
+ extras.putString("messageType", "notification");
+ extras.putString("tap", "background");
+ notificationStack.add(extras);
+ Log.d(TAG, "Notification message found on init: " + extras.toString());
+ }
+ }
+ defaultChannelId = getStringResource("default_notification_channel_id");
+ defaultChannelName = getStringResource("default_notification_channel_name");
+ createDefaultChannel();
+ }catch (Exception e){
+ handleExceptionWithoutContext(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ try{
+ if (action.equals("getId")) {
+ this.getId(callbackContext);
+ return true;
+ } else if (action.equals("getToken")) {
+ this.getToken(callbackContext);
+ return true;
+ } else if (action.equals("hasPermission")) {
+ this.hasPermission(callbackContext);
+ return true;
+ }else if (action.equals("subscribe")) {
+ this.subscribe(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("unsubscribe")) {
+ this.unsubscribe(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("isAutoInitEnabled")) {
+ isAutoInitEnabled(callbackContext);
+ return true;
+ } else if (action.equals("setAutoInitEnabled")) {
+ setAutoInitEnabled(callbackContext, args.getBoolean(0));
+ return true;
+ } else if (action.equals("unregister")) {
+ this.unregister(callbackContext);
+ return true;
+ } else if (action.equals("onMessageReceived")) {
+ this.onMessageReceived(callbackContext);
+ return true;
+ } else if (action.equals("onTokenRefresh")) {
+ this.onTokenRefresh(callbackContext);
+ return true;
+ } else if (action.equals("logEvent")) {
+ this.logEvent(callbackContext, args.getString(0), args.getJSONObject(1));
+ return true;
+ } else if (action.equals("logError")) {
+ this.logError(callbackContext, args);
+ return true;
+ }else if(action.equals("setCrashlyticsUserId")){
+ this.setCrashlyticsUserId(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("setScreenName")) {
+ this.setScreenName(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("setUserId")) {
+ this.setUserId(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("setUserProperty")) {
+ this.setUserProperty(callbackContext, args.getString(0), args.getString(1));
+ return true;
+ } else if (action.equals("activateFetched")) {
+ this.activateFetched(callbackContext);
+ return true;
+ } else if (action.equals("fetch")) {
+ if (args.length() > 0) {
+ this.fetch(callbackContext, args.getLong(0));
+ } else {
+ this.fetch(callbackContext);
+ }
+ return true;
+ } else if (action.equals("getByteArray")) {
+ this.getByteArray(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("getValue")) {
+ this.getValue(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("getInfo")) {
+ this.getInfo(callbackContext);
+ return true;
+ } else if (action.equals("setConfigSettings")) {
+ this.setConfigSettings(callbackContext, args.getJSONObject(0));
+ return true;
+ } else if (action.equals("setDefaults")) {
+ this.setDefaults(callbackContext, args.getJSONObject(0));
+ return true;
+ } else if (action.equals("verifyPhoneNumber")) {
+ this.verifyPhoneNumber(callbackContext, args);
+ return true;
+ } else if (action.equals("authenticateUserWithGoogle")) {
+ this.authenticateUserWithGoogle(callbackContext, args);
+ return true;
+ } else if (action.equals("authenticateUserWithApple")) {
+ this.authenticateUserWithApple(callbackContext, args);
+ return true;
+ } else if (action.equals("createUserWithEmailAndPassword")) {
+ this.createUserWithEmailAndPassword(callbackContext, args);
+ return true;
+ } else if (action.equals("signInUserWithEmailAndPassword")) {
+ this.signInUserWithEmailAndPassword(callbackContext, args);
+ return true;
+ } else if (action.equals("signInUserWithCustomToken")) {
+ this.signInUserWithCustomToken(callbackContext, args);
+ return true;
+ } else if (action.equals("signInUserAnonymously")) {
+ this.signInUserAnonymously(callbackContext);
+ return true;
+ } else if (action.equals("signInWithCredential")) {
+ this.signInWithCredential(callbackContext, args);
+ return true;
+ } else if (action.equals("linkUserWithCredential")) {
+ this.linkUserWithCredential(callbackContext, args);
+ return true;
+ } else if (action.equals("reauthenticateWithCredential")) {
+ this.reauthenticateWithCredential(callbackContext, args);
+ return true;
+ } else if (action.equals("isUserSignedIn")) {
+ this.isUserSignedIn(callbackContext, args);
+ return true;
+ } else if (action.equals("signOutUser")) {
+ this.signOutUser(callbackContext, args);
+ return true;
+ } else if (action.equals("getCurrentUser")) {
+ this.getCurrentUser(callbackContext, args);
+ return true;
+ } else if (action.equals("reloadCurrentUser")) {
+ this.reloadCurrentUser(callbackContext, args);
+ return true;
+ } else if (action.equals("updateUserProfile")) {
+ this.updateUserProfile(callbackContext, args);
+ return true;
+ } else if (action.equals("updateUserEmail")) {
+ this.updateUserEmail(callbackContext, args);
+ return true;
+ } else if (action.equals("sendUserEmailVerification")) {
+ this.sendUserEmailVerification(callbackContext, args);
+ return true;
+ } else if (action.equals("updateUserPassword")) {
+ this.updateUserPassword(callbackContext, args);
+ return true;
+ } else if (action.equals("sendUserPasswordResetEmail")) {
+ this.sendUserPasswordResetEmail(callbackContext, args);
+ return true;
+ } else if (action.equals("deleteUser")) {
+ this.deleteUser(callbackContext, args);
+ return true;
+ } else if (action.equals("startTrace")) {
+ this.startTrace(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("incrementCounter")) {
+ this.incrementCounter(callbackContext, args.getString(0), args.getString(1));
+ return true;
+ } else if (action.equals("stopTrace")) {
+ this.stopTrace(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("setAnalyticsCollectionEnabled")) {
+ this.setAnalyticsCollectionEnabled(callbackContext, args.getBoolean(0));
+ return true;
+ } else if (action.equals("isAnalyticsCollectionEnabled")) {
+ this.isAnalyticsCollectionEnabled(callbackContext);
+ return true;
+ } else if (action.equals("setPerformanceCollectionEnabled")) {
+ this.setPerformanceCollectionEnabled(callbackContext, args.getBoolean(0));
+ return true;
+ } else if (action.equals("isPerformanceCollectionEnabled")) {
+ this.isPerformanceCollectionEnabled(callbackContext);
+ return true;
+ } else if (action.equals("setCrashlyticsCollectionEnabled")) {
+ this.setCrashlyticsCollectionEnabled(callbackContext, args.getBoolean(0));
+ return true;
+ } else if (action.equals("isCrashlyticsCollectionEnabled")) {
+ this.isCrashlyticsCollectionEnabled(callbackContext);
+ return true;
+ } else if (action.equals("clearAllNotifications")) {
+ this.clearAllNotifications(callbackContext);
+ return true;
+ } else if (action.equals("logMessage")) {
+ logMessage(args, callbackContext);
+ return true;
+ } else if (action.equals("sendCrash")) {
+ sendCrash(args, callbackContext);
+ return true;
+ } else if (action.equals("createChannel")) {
+ this.createChannel(callbackContext, args.getJSONObject(0));
+ return true;
+ } else if (action.equals("deleteChannel")) {
+ this.deleteChannel(callbackContext, args.getString(0));
+ return true;
+ } else if (action.equals("listChannels")) {
+ this.listChannels(callbackContext);
+ return true;
+ } else if (action.equals("setDefaultChannel")) {
+ this.setDefaultChannel(callbackContext, args.getJSONObject(0));
+ return true;
+ } else if (action.equals("addDocumentToFirestoreCollection")) {
+ this.addDocumentToFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("setDocumentInFirestoreCollection")) {
+ this.setDocumentInFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("updateDocumentInFirestoreCollection")) {
+ this.updateDocumentInFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("deleteDocumentFromFirestoreCollection")) {
+ this.deleteDocumentFromFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("documentExistsInFirestoreCollection")) {
+ this.documentExistsInFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("fetchDocumentInFirestoreCollection")) {
+ this.fetchDocumentInFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("fetchFirestoreCollection")) {
+ this.fetchFirestoreCollection(args, callbackContext);
+ return true;
+ } else if (action.equals("grantPermission")
+ || action.equals("setBadgeNumber")
+ || action.equals("getBadgeNumber")
+ ) {
+ // Stubs for other platform methods
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true));
+ return true;
+ }else{
+ callbackContext.error("Invalid action: " + action);
+ return false;
+ }
+ }catch(Exception e){
+ handleExceptionWithContext(e, callbackContext);
+ }
+ return false;
+ }
+
+ @Override
+ public void onPause(boolean multitasking) {
+ FirebasePlugin.inBackground = true;
+ }
+
+ @Override
+ public void onResume(boolean multitasking) {
+ FirebasePlugin.inBackground = false;
+ }
+
+ @Override
+ public void onReset() {
+ FirebasePlugin.notificationCallbackContext = null;
+ FirebasePlugin.tokenRefreshCallbackContext = null;
+ FirebasePlugin.activityResultCallbackContext = null;
+ FirebasePlugin.authResultCallbackContext = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ FirebaseAuth.getInstance().removeAuthStateListener(authStateListener);
+ instance = null;
+ cordovaActivity = null;
+ cordovaInterface = null;
+ applicationContext = null;
+ super.onDestroy();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ try {
+ switch (requestCode) {
+ case GOOGLE_SIGN_IN:
+ Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
+ GoogleSignInAccount acct;
+ try{
+ acct = task.getResult(ApiException.class);
+ }catch (ApiException ae){
+ if(ae.getStatusCode() == 10){
+ throw new Exception("Unknown server client ID");
+ }else{
+ throw new Exception(CommonStatusCodes.getStatusCodeString(ae.getStatusCode()));
+ }
+ }
+ AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
+ String id = FirebasePlugin.instance.saveAuthCredential(credential);
+
+ JSONObject returnResults = new JSONObject();
+ returnResults.put("instantVerification", true);
+ returnResults.put("id", id);
+ FirebasePlugin.activityResultCallbackContext.success(returnResults);
+ break;
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, FirebasePlugin.activityResultCallbackContext);
+ }
+ }
+
+ /**
+ * Get a string from resources without importing the .R package
+ *
+ * @param name Resource Name
+ * @return Resource
+ */
+ private String getStringResource(String name) {
+ return applicationContext.getString(
+ applicationContext.getResources().getIdentifier(
+ name, "string", applicationContext.getPackageName()
+ )
+ );
+ }
+
+ private void onMessageReceived(final CallbackContext callbackContext) {
+ FirebasePlugin.notificationCallbackContext = callbackContext;
+ if (FirebasePlugin.notificationStack != null) {
+ for (Bundle bundle : FirebasePlugin.notificationStack) {
+ FirebasePlugin.sendMessage(bundle, applicationContext);
+ }
+ FirebasePlugin.notificationStack.clear();
+ }
+ }
+
+ private void onTokenRefresh(final CallbackContext callbackContext) {
+ FirebasePlugin.tokenRefreshCallbackContext = callbackContext;
+
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String currentToken = FirebaseInstanceId.getInstance().getToken();
+ if (currentToken != null) {
+ FirebasePlugin.sendToken(currentToken);
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public static void sendMessage(Bundle bundle, Context context) {
+ if (!FirebasePlugin.hasNotificationsCallback()) {
+ String packageName = context.getPackageName();
+ if (FirebasePlugin.notificationStack == null) {
+ FirebasePlugin.notificationStack = new ArrayList<Bundle>();
+ }
+ notificationStack.add(bundle);
+
+ return;
+ }
+
+ final CallbackContext callbackContext = FirebasePlugin.notificationCallbackContext;
+ if(bundle != null){
+ // Pass the message bundle to the receiver manager so any registered receivers can decide to handle it
+ boolean wasHandled = FirebasePluginMessageReceiverManager.sendMessage(bundle);
+ if (wasHandled) {
+ Log.d(TAG, "Message bundle was handled by a registered receiver");
+ }else if (callbackContext != null) {
+ JSONObject json = new JSONObject();
+ Set<String> keys = bundle.keySet();
+ for (String key : keys) {
+ try {
+ json.put(key, bundle.get(key));
+ } catch (JSONException e) {
+ handleExceptionWithContext(e, callbackContext);
+ return;
+ }
+ }
+
+ PluginResult pluginresult = new PluginResult(PluginResult.Status.OK, json);
+ pluginresult.setKeepCallback(true);
+ callbackContext.sendPluginResult(pluginresult);
+ }
+ }
+ }
+
+ public static void sendToken(String token) {
+ if (FirebasePlugin.tokenRefreshCallbackContext == null) {
+ return;
+ }
+
+ final CallbackContext callbackContext = FirebasePlugin.tokenRefreshCallbackContext;
+ if (callbackContext != null && token != null) {
+ PluginResult pluginresult = new PluginResult(PluginResult.Status.OK, token);
+ pluginresult.setKeepCallback(true);
+ callbackContext.sendPluginResult(pluginresult);
+ }
+ }
+
+ public static boolean inBackground() {
+ return FirebasePlugin.inBackground;
+ }
+
+ public static boolean hasNotificationsCallback() {
+ return FirebasePlugin.notificationCallbackContext != null;
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ try {
+ super.onNewIntent(intent);
+ final Bundle data = intent.getExtras();
+ if (data != null && data.containsKey("google.message_id")) {
+ data.putString("messageType", "notification");
+ data.putString("tap", "background");
+ Log.d(TAG, "Notification message on new intent: " + data.toString());
+ FirebasePlugin.sendMessage(data, applicationContext);
+ }
+ }catch (Exception e){
+ handleExceptionWithoutContext(e);
+ }
+ }
+
+
+ private void getId(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String id = FirebaseInstanceId.getInstance().getId();
+ callbackContext.success(id);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void getToken(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String token = FirebaseInstanceId.getInstance().getToken();
+ callbackContext.success(token);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void hasPermission(final CallbackContext callbackContext) {
+ if(cordovaActivity == null) return;
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(cordovaActivity);
+ boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled();
+ callbackContext.success(areNotificationsEnabled ? 1 : 0);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void subscribe(final CallbackContext callbackContext, final String topic) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ handleTaskOutcome(FirebaseMessaging.getInstance().subscribeToTopic(topic), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void unsubscribe(final CallbackContext callbackContext, final String topic) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ handleTaskOutcome(FirebaseMessaging.getInstance().unsubscribeFromTopic(topic), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void unregister(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseInstanceId.getInstance().deleteInstanceId();
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void isAutoInitEnabled(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ boolean isEnabled = FirebaseMessaging.getInstance().isAutoInitEnabled();
+ callbackContext.success(isEnabled ? 1 : 0);
+ } catch (Exception e) {
+ logExceptionToCrashlytics(e);
+ callbackContext.error(e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void setAutoInitEnabled(final CallbackContext callbackContext, final boolean enabled) {
+ final FirebasePlugin self = this;
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseMessaging.getInstance().setAutoInitEnabled(enabled);
+ callbackContext.success();
+ } catch (Exception e) {
+ logExceptionToCrashlytics(e);
+ e.printStackTrace();
+ callbackContext.error(e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void logEvent(final CallbackContext callbackContext, final String name, final JSONObject params)
+ throws JSONException {
+ final Bundle bundle = new Bundle();
+ Iterator iter = params.keys();
+ while (iter.hasNext()) {
+ String key = (String) iter.next();
+ Object value = params.get(key);
+
+ if (value instanceof Integer || value instanceof Double) {
+ bundle.putFloat(key, ((Number) value).floatValue());
+ } else {
+ bundle.putString(key, value.toString());
+ }
+ }
+
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ mFirebaseAnalytics.logEvent(name, bundle);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void logError(final CallbackContext callbackContext, final JSONArray args) throws JSONException {
+ final String message = args.getString(0);
+
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ if(isCrashlyticsEnabled()) {
+ // We can optionally be passed a stack trace generated by stacktrace.js.
+ if (args.length() == 2) {
+ JSONArray stackTrace = args.getJSONArray(1);
+ StackTraceElement[] trace = new StackTraceElement[stackTrace.length()];
+ for (int i = 0; i < stackTrace.length(); i++) {
+ JSONObject elem = stackTrace.getJSONObject(i);
+ trace[i] = new StackTraceElement(
+ "",
+ elem.optString("functionName", "(anonymous function)"),
+ elem.optString("fileName", "(unknown file)"),
+ elem.optInt("lineNumber", -1)
+ );
+ }
+
+ Exception e = new JavaScriptException(message);
+ e.setStackTrace(trace);
+ logExceptionToCrashlytics(e);
+ } else {
+ logExceptionToCrashlytics(new JavaScriptException(message));
+ }
+
+ Log.e(TAG, message);
+ callbackContext.success(1);
+ }else{
+ callbackContext.error("Cannot log error - Crashlytics collection is disabled");
+ }
+ } catch (Exception e) {
+ logExceptionToCrashlytics(e);
+ callbackContext.error(e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void logMessage(final JSONArray data,
+ final CallbackContext callbackContext) {
+
+ if(isCrashlyticsEnabled()){
+ String message = data.optString(0);
+ logMessageToCrashlytics(message);
+ callbackContext.success();
+ }else{
+ callbackContext.error("Cannot log message - Crashlytics collection is disabled");
+ }
+ }
+
+ private void sendCrash(final JSONArray data,
+ final CallbackContext callbackContext) {
+
+ cordovaActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ throw new RuntimeException("This is a crash");
+ }
+ });
+ }
+
+
+ private void setCrashlyticsUserId(final CallbackContext callbackContext, final String userId) {
+ cordovaActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ try {
+ if(isCrashlyticsEnabled()){
+ firebaseCrashlytics.setUserId(userId);
+ callbackContext.success();
+ }else{
+ callbackContext.error("Cannot set Crashlytics user ID - Crashlytics collection is disabled");
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setScreenName(final CallbackContext callbackContext, final String name) {
+ // This must be called on the main thread
+ cordovaActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ try {
+ mFirebaseAnalytics.setCurrentScreen(cordovaActivity, name, null);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setUserId(final CallbackContext callbackContext, final String id) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ mFirebaseAnalytics.setUserId(id);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setUserProperty(final CallbackContext callbackContext, final String name, final String value) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ mFirebaseAnalytics.setUserProperty(name, value);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void activateFetched(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ final boolean activated = FirebaseRemoteConfig.getInstance().activateFetched();
+ callbackContext.success(String.valueOf(activated));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void fetch(CallbackContext callbackContext) {
+ fetch(callbackContext, FirebaseRemoteConfig.getInstance().fetch());
+ }
+
+ private void fetch(CallbackContext callbackContext, long cacheExpirationSeconds) {
+ fetch(callbackContext, FirebaseRemoteConfig.getInstance().fetch(cacheExpirationSeconds));
+ }
+
+ private void fetch(final CallbackContext callbackContext, final Task<Void> task) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ handleTaskOutcome(task, callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void getByteArray(final CallbackContext callbackContext, final String key) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ byte[] bytes = FirebaseRemoteConfig.getInstance().getByteArray(key);
+ JSONObject object = new JSONObject();
+ object.put("base64", Base64.encodeToString(bytes, Base64.DEFAULT));
+ object.put("array", new JSONArray(bytes));
+ callbackContext.success(object);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void getValue(final CallbackContext callbackContext, final String key) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseRemoteConfigValue value = FirebaseRemoteConfig.getInstance().getValue(key);
+ callbackContext.success(value.asString());
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void getInfo(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseRemoteConfigInfo remoteConfigInfo = FirebaseRemoteConfig.getInstance().getInfo();
+ JSONObject info = new JSONObject();
+
+ JSONObject settings = new JSONObject();
+ settings.put("developerModeEnabled", remoteConfigInfo.getConfigSettings().isDeveloperModeEnabled());
+ info.put("configSettings", settings);
+
+ info.put("fetchTimeMillis", remoteConfigInfo.getFetchTimeMillis());
+ info.put("lastFetchStatus", remoteConfigInfo.getLastFetchStatus());
+
+ callbackContext.success(info);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setConfigSettings(final CallbackContext callbackContext, final JSONObject config) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ boolean devMode = config.getBoolean("developerModeEnabled");
+ FirebaseRemoteConfigSettings.Builder settings = new FirebaseRemoteConfigSettings.Builder()
+ .setDeveloperModeEnabled(devMode);
+ FirebaseRemoteConfig.getInstance().setConfigSettings(settings.build());
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setDefaults(final CallbackContext callbackContext, final JSONObject defaults) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseRemoteConfig.getInstance().setDefaults(defaultsToMap(defaults));
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private static Map<String, Object> defaultsToMap(JSONObject object) throws JSONException {
+ final Map<String, Object> map = new HashMap<String, Object>();
+
+ for (Iterator<String> keys = object.keys(); keys.hasNext(); ) {
+ String key = keys.next();
+ Object value = object.get(key);
+
+ if (value instanceof Integer) {
+ //setDefaults() should take Longs
+ value = new Long((Integer) value);
+ } else if (value instanceof JSONArray) {
+ JSONArray array = (JSONArray) value;
+ if (array.length() == 1 && array.get(0) instanceof String) {
+ //parse byte[] as Base64 String
+ value = Base64.decode(array.getString(0), Base64.DEFAULT);
+ } else {
+ //parse byte[] as numeric array
+ byte[] bytes = new byte[array.length()];
+ for (int i = 0; i < array.length(); i++)
+ bytes[i] = (byte) array.getInt(i);
+ value = bytes;
+ }
+ }
+
+ map.put(key, value);
+ }
+ return map;
+ }
+
+
+ public void isUserSignedIn(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ boolean isSignedIn = FirebaseAuth.getInstance().getCurrentUser() != null;
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, isSignedIn));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void signOutUser(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+ // Sign out of Firebase
+ FirebaseAuth.getInstance().signOut();
+
+ // Try to sign out of Google
+ try{
+ GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).build();
+ GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(cordovaActivity, gso);
+ handleTaskOutcome(mGoogleSignInClient.signOut(), callbackContext);
+ }catch(Exception googleSignOutException){
+ callbackContext.success();
+ }
+
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void getCurrentUser(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+ extractAndReturnUserInfo(callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void reloadCurrentUser(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+ user.reload()
+ .addOnSuccessListener(new OnSuccessListener<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ try {
+ extractAndReturnUserInfo(callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void extractAndReturnUserInfo(final CallbackContext callbackContext) throws Exception{
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ JSONObject returnResults = new JSONObject();
+ returnResults.put("name", user.getDisplayName());
+ returnResults.put("email", user.getEmail());
+ returnResults.put("emailIsVerified", user.isEmailVerified());
+ returnResults.put("phoneNumber", user.getPhoneNumber());
+ returnResults.put("photoUrl", user.getPhotoUrl() == null ? null : user.getPhotoUrl().toString());
+ returnResults.put("uid", user.getUid());
+ returnResults.put("providerId", user.getIdToken(false).getResult().getSignInProvider());
+ returnResults.put("isAnonymous", user.isAnonymous());
+
+ user.getIdToken(true).addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
+ @Override
+ public void onSuccess(GetTokenResult result) {
+ try {
+ String idToken = result.getToken();
+ returnResults.put("idToken", idToken);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, returnResults));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void updateUserProfile(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+
+ JSONObject profile = args.getJSONObject(0);
+ UserProfileChangeRequest profileUpdates;
+ if(profile.has("name") && profile.has("photoUri")){
+ profileUpdates = new UserProfileChangeRequest.Builder()
+ .setDisplayName(profile.getString("name"))
+ .setPhotoUri(Uri.parse(profile.getString("photoUri")))
+ .build();
+ }else if(profile.has("name")){
+ profileUpdates = new UserProfileChangeRequest.Builder()
+ .setDisplayName(profile.getString("name"))
+ .build();
+ }else if(profile.has("photoUri")){
+ profileUpdates = new UserProfileChangeRequest.Builder()
+ .setPhotoUri(Uri.parse(profile.getString("photoUri")))
+ .build();
+ }else{
+ callbackContext.error("'name' and/or 'photoUri' keys must be specified in the profile object");
+ return;
+ }
+
+ handleTaskOutcome(user.updateProfile(profileUpdates), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void updateUserEmail(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+
+ String email = args.getString(0);
+ handleTaskOutcome(user.updateEmail(email), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void sendUserEmailVerification(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+
+ handleTaskOutcome(user.sendEmailVerification(), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void updateUserPassword(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+
+ String password = args.getString(0);
+ handleTaskOutcome(user.updatePassword(password), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void sendUserPasswordResetEmail(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseAuth auth = FirebaseAuth.getInstance();
+ String email = args.getString(0);
+ handleTaskOutcome(auth.sendPasswordResetEmail(email), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void deleteUser(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+ handleTaskOutcome(user.delete(), callbackContext);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void reauthenticateWithCredential(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ if(user == null){
+ callbackContext.error("No user is currently signed");
+ return;
+ }
+
+ JSONObject jsonCredential = args.getJSONObject(0);
+ if(!FirebasePlugin.instance.isValidJsonCredential(jsonCredential)){
+ callbackContext.error("No auth credentials specified");
+ return;
+ }
+
+ AuthCredential authCredential = FirebasePlugin.instance.obtainAuthCredential(jsonCredential);
+ if(authCredential != null){
+ handleTaskOutcome(user.reauthenticate(authCredential), callbackContext);
+ return;
+ }
+
+ OAuthProvider authProvider = FirebasePlugin.instance.obtainAuthProvider(jsonCredential);
+ if(authProvider != null){
+ FirebasePlugin.instance.authResultCallbackContext = callbackContext;
+ user.startActivityForReauthenticateWithProvider(FirebasePlugin.cordovaActivity, authProvider)
+ .addOnSuccessListener(new AuthResultOnSuccessListener())
+ .addOnFailureListener(new AuthResultOnFailureListener());
+ return;
+ }
+
+ //ELSE
+ callbackContext.error("Specified native auth credential id does not exist");
+
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+
+
+ public void signInWithCredential(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ JSONObject jsonCredential = args.getJSONObject(0);
+ if(!FirebasePlugin.instance.isValidJsonCredential(jsonCredential)){
+ callbackContext.error("No auth credentials specified");
+ return;
+ }
+
+ AuthCredential authCredential = FirebasePlugin.instance.obtainAuthCredential(jsonCredential);
+ if(authCredential != null){
+ FirebaseAuth.getInstance().signInWithCredential(authCredential).addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ return;
+ }
+
+ OAuthProvider authProvider = FirebasePlugin.instance.obtainAuthProvider(jsonCredential);
+ if(authProvider != null){
+ FirebasePlugin.instance.authResultCallbackContext = callbackContext;
+ FirebaseAuth.getInstance().startActivityForSignInWithProvider(FirebasePlugin.cordovaActivity, authProvider)
+ .addOnSuccessListener(new AuthResultOnSuccessListener())
+ .addOnFailureListener(new AuthResultOnFailureListener());
+ return;
+ }
+
+ //ELSE
+ callbackContext.error("Specified native auth credential id does not exist");
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void linkUserWithCredential(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ JSONObject jsonCredential = args.getJSONObject(0);
+ if(!FirebasePlugin.instance.isValidJsonCredential(jsonCredential)){
+ callbackContext.error("No auth credentials specified");
+ return;
+ }
+
+ AuthCredential authCredential = FirebasePlugin.instance.obtainAuthCredential(jsonCredential);
+ if(authCredential != null){
+ FirebaseAuth.getInstance().getCurrentUser().linkWithCredential(authCredential).addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ return;
+ }
+
+ //ELSE
+ callbackContext.error("Specified native auth credential id does not exist");
+
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private boolean isValidJsonCredential(JSONObject jsonCredential) throws JSONException{
+ return jsonCredential.has("id") || (jsonCredential.has("verificationId") && jsonCredential.has("code"));
+ }
+
+ private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;
+
+ public void verifyPhoneNumber(
+ final CallbackContext callbackContext,
+ final JSONArray args
+ ) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
+ @Override
+ public void onVerificationCompleted(PhoneAuthCredential credential) {
+ // This callback will be invoked in two situations:
+ // 1 - Instant verification. In some cases the phone number can be instantly
+ // verified without needing to send or enter a verification code.
+ // 2 - Auto-retrieval. On some devices Google Play services can automatically
+ // detect the incoming verification SMS and perform verificaiton without
+ // user action.
+ Log.d(TAG, "success: verifyPhoneNumber.onVerificationCompleted");
+
+ String id = FirebasePlugin.instance.saveAuthCredential((AuthCredential) credential);
+
+ JSONObject returnResults = new JSONObject();
+ try {
+ returnResults.put("instantVerification", true);
+ returnResults.put("id", id);
+ } catch(JSONException e){
+ handleExceptionWithContext(e, callbackContext);
+ return;
+ }
+ PluginResult pluginresult = new PluginResult(PluginResult.Status.OK, returnResults);
+ pluginresult.setKeepCallback(true);
+ callbackContext.sendPluginResult(pluginresult);
+ }
+
+ @Override
+ public void onVerificationFailed(FirebaseException e) {
+ // This callback is invoked in an invalid request for verification is made,
+ // for instance if the the phone number format is not valid.
+ Log.w(TAG, "failed: verifyPhoneNumber.onVerificationFailed ", e);
+
+ String errorMsg;
+ if (e instanceof FirebaseAuthInvalidCredentialsException) {
+ // Invalid request
+ errorMsg = "Invalid phone number";
+ } else if (e instanceof FirebaseTooManyRequestsException) {
+ // The SMS quota for the project has been exceeded
+ errorMsg = "The SMS quota for the project has been exceeded";
+ }else{
+ errorMsg = e.getMessage();
+ }
+ callbackContext.error(errorMsg);
+ }
+
+ @Override
+ public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) {
+ // The SMS verification code has been sent to the provided phone number, we
+ // now need to ask the user to enter the code and then construct a credential
+ // by combining the code with a verification ID [(in app)].
+ Log.d(TAG, "success: verifyPhoneNumber.onCodeSent");
+
+ JSONObject returnResults = new JSONObject();
+ try {
+ returnResults.put("verificationId", verificationId);
+ returnResults.put("instantVerification", false);
+ } catch (JSONException e) {
+ handleExceptionWithContext(e, callbackContext);
+ return;
+ }
+ PluginResult pluginresult = new PluginResult(PluginResult.Status.OK, returnResults);
+ pluginresult.setKeepCallback(true);
+ callbackContext.sendPluginResult(pluginresult);
+ }
+ };
+
+ String number = args.getString(0);
+ int timeOutDuration = args.getInt(1);
+ String smsCode = args.getString(2);
+
+ if(smsCode != null && smsCode != "null"){
+ FirebaseAuth.getInstance().getFirebaseAuthSettings().setAutoRetrievedSmsCodeForPhoneNumber(number, smsCode);
+ }
+
+ PhoneAuthProvider.getInstance().verifyPhoneNumber(number, // Phone number to verify
+ timeOutDuration, // Timeout duration
+ TimeUnit.SECONDS, // Unit of timeout
+ cordovaActivity, // Activity (for callback binding)
+ mCallbacks); // OnVerificationStateChangedCallbacks
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void createUserWithEmailAndPassword(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String email = args.getString(0);
+ String password = args.getString(1);
+
+ if(email == null || email.equals("")){
+ callbackContext.error("User email address must be specified");
+ return;
+ }
+
+ if(password == null || password.equals("")){
+ callbackContext.error("User password must be specified");
+ return;
+ }
+
+ FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password).addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void signInUserWithEmailAndPassword(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String email = args.getString(0);
+ String password = args.getString(1);
+
+ if(email == null || email.equals("")){
+ callbackContext.error("User email address must be specified");
+ return;
+ }
+
+ if(password == null || password.equals("")){
+ callbackContext.error("User password must be specified");
+ return;
+ }
+
+ FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password).addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+
+ public void authenticateUserWithGoogle(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String clientId = args.getString(0);
+
+ GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestIdToken(clientId)
+ .requestEmail()
+ .build();
+
+ GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(FirebasePlugin.instance.cordovaActivity, gso);
+ Intent signInIntent = mGoogleSignInClient.getSignInIntent();
+ FirebasePlugin.activityResultCallbackContext = callbackContext;
+ FirebasePlugin.instance.cordovaInterface.startActivityForResult(FirebasePlugin.instance, signInIntent, GOOGLE_SIGN_IN);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void authenticateUserWithApple(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String locale = args.getString(0);
+ OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
+ if(locale != null){
+ provider.addCustomParameter("locale", locale);
+ }
+ Task<AuthResult> pending = FirebaseAuth.getInstance().getPendingAuthResult();
+ if (pending != null) {
+ callbackContext.error("Auth result is already pending");
+ pending
+ .addOnSuccessListener(new AuthResultOnSuccessListener())
+ .addOnFailureListener(new AuthResultOnFailureListener());
+ } else {
+ String id = FirebasePlugin.instance.saveAuthProvider(provider.build());;
+ JSONObject returnResults = new JSONObject();
+ returnResults.put("instantVerification", true);
+ returnResults.put("id", id);
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, returnResults));
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void signInUserWithCustomToken(final CallbackContext callbackContext, final JSONArray args){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String customToken = args.getString(0);
+
+ if(customToken == null || customToken.equals("")){
+ callbackContext.error("Custom token must be specified");
+ return;
+ }
+
+ FirebaseAuth.getInstance().signInWithCustomToken(customToken).addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void signInUserAnonymously(final CallbackContext callbackContext){
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebaseAuth.getInstance().signInAnonymously().addOnCompleteListener(cordova.getActivity(), new AuthResultOnCompleteListener(callbackContext));
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ //
+ // Firebase Performace
+ //
+
+ private HashMap<String, Trace> traces = new HashMap<String, Trace>();
+
+ private void startTrace(final CallbackContext callbackContext, final String name) {
+ final FirebasePlugin self = this;
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+
+ Trace myTrace = null;
+ if (self.traces.containsKey(name)) {
+ myTrace = self.traces.get(name);
+ }
+
+ if (myTrace == null) {
+ myTrace = FirebasePerformance.getInstance().newTrace(name);
+ myTrace.start();
+ self.traces.put(name, myTrace);
+ }
+
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void incrementCounter(final CallbackContext callbackContext, final String name, final String counterNamed) {
+ final FirebasePlugin self = this;
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+
+ Trace myTrace = null;
+ if (self.traces.containsKey(name)) {
+ myTrace = self.traces.get(name);
+ }
+
+ if (myTrace != null && myTrace instanceof Trace) {
+ myTrace.incrementMetric(counterNamed, 1);
+ callbackContext.success();
+ } else {
+ callbackContext.error("Trace not found");
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void stopTrace(final CallbackContext callbackContext, final String name) {
+ final FirebasePlugin self = this;
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+
+ Trace myTrace = null;
+ if (self.traces.containsKey(name)) {
+ myTrace = self.traces.get(name);
+ }
+
+ if (myTrace != null && myTrace instanceof Trace) { //
+ myTrace.stop();
+ self.traces.remove(name);
+ callbackContext.success();
+ } else {
+ callbackContext.error("Trace not found");
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void setAnalyticsCollectionEnabled(final CallbackContext callbackContext, final boolean enabled) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ mFirebaseAnalytics.setAnalyticsCollectionEnabled(enabled);
+ setPreference(ANALYTICS_COLLECTION_ENABLED, enabled);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void isAnalyticsCollectionEnabled(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ callbackContext.success(getPreference(ANALYTICS_COLLECTION_ENABLED) ? 1 : 0);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void setPerformanceCollectionEnabled(final CallbackContext callbackContext, final boolean enabled) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ FirebasePerformance.getInstance().setPerformanceCollectionEnabled(enabled);
+ setPreference(PERFORMANCE_COLLECTION_ENABLED, enabled);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void isPerformanceCollectionEnabled(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ callbackContext.success(getPreference(PERFORMANCE_COLLECTION_ENABLED) ? 1 : 0);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void setCrashlyticsCollectionEnabled(final CallbackContext callbackContext, final boolean enabled) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ firebaseCrashlytics.setCrashlyticsCollectionEnabled(enabled);
+ setPreference(CRASHLYTICS_COLLECTION_ENABLED, enabled);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private void isCrashlyticsCollectionEnabled(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ callbackContext.success(isCrashlyticsEnabled() ? 1 : 0);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private boolean isCrashlyticsEnabled(){
+ return getPreference(CRASHLYTICS_COLLECTION_ENABLED);
+ }
+
+ public void clearAllNotifications(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ NotificationManager nm = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.cancelAll();
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void createChannel(final CallbackContext callbackContext, final JSONObject options) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ createChannel(options);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ protected static NotificationChannel createChannel(final JSONObject options) throws JSONException {
+ NotificationChannel channel = null;
+ // only call on Android O and above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String id = options.getString("id");
+ Log.i(TAG, "Creating channel id="+id);
+
+ if(channelExists(id)){
+ deleteChannel(id);
+ }
+
+ NotificationManager nm = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ String packageName = cordovaActivity.getPackageName();
+
+ String name = options.optString("name", "");
+ Log.d(TAG, "Channel "+id+" - name="+name);
+
+ int importance = options.optInt("importance", NotificationManager.IMPORTANCE_HIGH);
+ Log.d(TAG, "Channel "+id+" - importance="+importance);
+
+ channel = new NotificationChannel(id,
+ name,
+ importance);
+
+ // Description
+ String description = options.optString("description", "");
+ Log.d(TAG, "Channel "+id+" - description="+description);
+ channel.setDescription(description);
+
+ // Light
+ boolean light = options.optBoolean("light", true);
+ Log.d(TAG, "Channel "+id+" - light="+light);
+ channel.enableLights(light);
+
+ int lightColor = options.optInt("lightColor", -1);
+ if (lightColor != -1) {
+ Log.d(TAG, "Channel "+id+" - lightColor="+lightColor);
+ channel.setLightColor(lightColor);
+ }
+
+ // Visibility
+ int visibility = options.optInt("visibility", NotificationCompat.VISIBILITY_PUBLIC);
+ Log.d(TAG, "Channel "+id+" - visibility="+visibility);
+ channel.setLockscreenVisibility(visibility);
+
+ // Badge
+ boolean badge = options.optBoolean("badge", true);
+ Log.d(TAG, "Channel "+id+" - badge="+badge);
+ channel.setShowBadge(badge);
+
+ // Sound
+ String sound = options.optString("sound", "default");
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+ if ("ringtone".equals(sound)) {
+ channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), audioAttributes);
+ Log.d(TAG, "Channel "+id+" - sound=ringtone");
+ } else if (sound != null && !sound.contentEquals("default")) {
+ Uri soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + packageName + "/raw/" + sound);
+ channel.setSound(soundUri, audioAttributes);
+ Log.d(TAG, "Channel "+id+" - sound="+sound);
+ } else if (sound != "false"){
+ channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), audioAttributes);
+ Log.d(TAG, "Channel "+id+" - sound=default");
+ }else{
+ Log.d(TAG, "Channel "+id+" - sound=none");
+ }
+
+ // Vibration: if vibration setting is an array set vibration pattern, else set enable vibration.
+ JSONArray pattern = options.optJSONArray("vibration");
+ if (pattern != null) {
+ int patternLength = pattern.length();
+ long[] patternArray = new long[patternLength];
+ for (int i = 0; i < patternLength; i++) {
+ patternArray[i] = pattern.optLong(i);
+ }
+ channel.enableVibration(true);
+ channel.setVibrationPattern(patternArray);
+ Log.d(TAG, "Channel "+id+" - vibrate="+pattern);
+ } else {
+ boolean vibrate = options.optBoolean("vibration", true);
+ channel.enableVibration(vibrate);
+ Log.d(TAG, "Channel "+id+" - vibrate="+vibrate);
+ }
+
+ // Create channel
+ nm.createNotificationChannel(channel);
+ }
+ return channel;
+ }
+
+ protected static void createDefaultChannel() throws JSONException {
+ JSONObject options = new JSONObject();
+ options.put("id", defaultChannelId);
+ options.put("name", defaultChannelName);
+ createDefaultChannel(options);
+ }
+
+ protected static void createDefaultChannel(final JSONObject options) throws JSONException {
+ defaultNotificationChannel = createChannel(options);
+ }
+
+ public void setDefaultChannel(final CallbackContext callbackContext, final JSONObject options) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ deleteChannel(defaultChannelId);
+
+ String id = options.optString("id", null);
+ if(id != null){
+ defaultChannelId = id;
+ }
+
+ String name = options.optString("name", null);
+ if(name != null){
+ defaultChannelName = name;
+ }
+ createDefaultChannel(options);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public void deleteChannel(final CallbackContext callbackContext, final String channelID) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ deleteChannel(channelID);
+ callbackContext.success();
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ protected static void deleteChannel(final String channelID){
+ // only call on Android O and above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationManager nm = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.deleteNotificationChannel(channelID);
+ }
+ }
+
+ public void listChannels(final CallbackContext callbackContext) {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ List<NotificationChannel> notificationChannels = listChannels();
+ JSONArray channels = new JSONArray();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ for (NotificationChannel notificationChannel : notificationChannels) {
+ JSONObject channel = new JSONObject();
+ channel.put("id", notificationChannel.getId());
+ channel.put("name", notificationChannel.getName());
+ channels.put(channel);
+ }
+ }
+ callbackContext.success(channels);
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ public static List<NotificationChannel> listChannels(){
+ List<NotificationChannel> notificationChannels = null;
+ // only call on Android O and above
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationManager nm = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationChannels = nm.getNotificationChannels();
+ }
+ return notificationChannels;
+ }
+
+ public static boolean channelExists(String channelId){
+ boolean exists = false;
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
+ List<NotificationChannel> notificationChannels = FirebasePlugin.listChannels();
+ if(notificationChannels != null){
+ for (NotificationChannel notificationChannel : notificationChannels) {
+ if(notificationChannel.getId().equals(channelId)){
+ exists = true;
+ }
+ }
+ }
+ }
+ return exists;
+ }
+
+ //
+ // Firestore
+ //
+ private void addDocumentToFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String jsonDoc = args.getString(0);
+ String collection = args.getString(1);
+
+ firestore.collection(collection)
+ .add(jsonStringToMap(jsonDoc))
+ .addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
+ @Override
+ public void onSuccess(DocumentReference documentReference) {
+ callbackContext.success(documentReference.getId());
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void setDocumentInFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String documentId = args.getString(0);
+ String jsonDoc = args.getString(1);
+ String collection = args.getString(2);
+
+ firestore.collection(collection).document(documentId)
+ .set(jsonStringToMap(jsonDoc))
+ .addOnSuccessListener(new OnSuccessListener<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ callbackContext.success();
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void updateDocumentInFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String documentId = args.getString(0);
+ String jsonDoc = args.getString(1);
+ String collection = args.getString(2);
+
+ firestore.collection(collection).document(documentId)
+ .update(jsonStringToMap(jsonDoc))
+ .addOnSuccessListener(new OnSuccessListener<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ callbackContext.success();
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void deleteDocumentFromFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String documentId = args.getString(0);
+ String collection = args.getString(1);
+
+ firestore.collection(collection).document(documentId)
+ .delete()
+ .addOnSuccessListener(new OnSuccessListener<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ callbackContext.success();
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void documentExistsInFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String documentId = args.getString(0);
+ String collection = args.getString(1);
+
+ firestore.collection(collection).document(documentId)
+ .get()
+ .addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
+ @Override
+ public void onComplete(@NonNull Task<DocumentSnapshot> task) {
+ try {
+ if (task.isSuccessful()) {
+ DocumentSnapshot document = task.getResult();
+ callbackContext.success(document != null && document.getData() != null ? 1 : 0);
+ } else {
+ Exception e = task.getException();
+ if(e != null){
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void fetchDocumentInFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String documentId = args.getString(0);
+ String collection = args.getString(1);
+
+ firestore.collection(collection).document(documentId)
+ .get()
+ .addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
+ @Override
+ public void onComplete(@NonNull Task<DocumentSnapshot> task) {
+ try {
+ if (task.isSuccessful()) {
+ DocumentSnapshot document = task.getResult();
+ if (document != null && document.getData() != null) {
+ JSONObject jsonDoc = mapToJsonObject(document.getData());
+ callbackContext.success(jsonDoc);
+ } else {
+ callbackContext.error("No document found in collection");
+ }
+ } else {
+ Exception e = task.getException();
+ if(e != null){
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ })
+ .addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+ private void fetchFirestoreCollection(JSONArray args, CallbackContext callbackContext) throws JSONException {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ try {
+ String collection = args.getString(0);
+ JSONArray filters = args.getJSONArray(1);
+ Query query = firestore.collection(collection);
+
+ for(int i = 0; i < filters.length(); i++) {
+ JSONArray filter = filters.getJSONArray(i);
+ switch(filter.getString(0)) {
+ case "where":
+ if (Objects.equals(filter.getString(2), new String("=="))) {
+ query = query.whereEqualTo(filter.getString(1), filter.getString(3));
+ }
+ if (Objects.equals(filter.getString(2), new String("<"))) {
+ query = query.whereLessThan(filter.getString(1), filter.getString(3));
+ }
+ if (Objects.equals(filter.getString(2), new String(">"))) {
+ query = query.whereGreaterThan(filter.getString(1), filter.getString(3));
+ }
+ if (Objects.equals(filter.getString(2), new String("<="))) {
+ query = query.whereLessThanOrEqualTo(filter.getString(1), filter.getString(3));
+ }
+ if (Objects.equals(filter.getString(2), new String(">="))) {
+ query = query.whereGreaterThanOrEqualTo(filter.getString(1), filter.getString(3));
+ }
+ if (Objects.equals(filter.getString(2), new String("array-contains"))) {
+ query = query.whereArrayContains(filter.getString(1), filter.getString(3));
+ }
+ break;
+ case "orderBy":
+ Direction direction = Direction.ASCENDING;
+ if (Objects.equals(filter.getString(2), new String("desc"))) {
+ direction = Direction.DESCENDING;
+ }
+ query = query.orderBy(filter.getString(1), direction);
+ break;
+ case "startAt":
+ query = query.startAt(filter.getString(1));
+ break;
+ case "endAt":
+ query = query.endAt(filter.getString(1));
+ break;
+ case "limit":
+ query = query.limit(filter.getLong(1));
+ break;
+ }
+ }
+
+ query.get()
+ .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
+ @Override
+ public void onComplete(@NonNull Task<QuerySnapshot> task) {
+ try {
+ if (task.isSuccessful()) {
+ JSONObject jsonDocs = new JSONObject();
+ for (QueryDocumentSnapshot document : task.getResult()) {
+ jsonDocs.put(document.getId(), mapToJsonObject(document.getData()));
+ }
+ callbackContext.success(jsonDocs);
+ } else {
+ handleExceptionWithContext(task.getException(), callbackContext);
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+ });
+ }
+
+
+ protected static void handleExceptionWithContext(Exception e, CallbackContext context) {
+ String msg = e.toString();
+ Log.e(TAG, msg);
+ instance.logExceptionToCrashlytics(e);
+ context.error(msg);
+ }
+
+ protected static void handleExceptionWithoutContext(Exception e){
+ String msg = e.toString();
+ Log.e(TAG, msg);
+ if (instance != null) {
+ instance.logExceptionToCrashlytics(e);
+ instance.logErrorToWebview(msg);
+ }
+ }
+
+ protected void logErrorToWebview(String msg){
+ Log.e(TAG, msg);
+ executeGlobalJavascript("console.error(\""+TAG+"[native]: "+escapeDoubleQuotes(msg)+"\")");
+ }
+
+ private String escapeDoubleQuotes(String string){
+ String escapedString = string.replace("\"", "\\\"");
+ escapedString = escapedString.replace("%22", "\\%22");
+ return escapedString;
+ }
+
+ private void executeGlobalJavascript(final String jsString){
+ if(cordovaActivity == null) return;
+ cordovaActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ webView.loadUrl("javascript:" + jsString);
+ }
+ });
+ }
+
+ private String saveAuthCredential(AuthCredential authCredential){
+ String id = this.generateId();
+ this.authCredentials.put(id, authCredential);
+ return id;
+ }
+
+ private String saveAuthProvider(OAuthProvider authProvider){
+ String id = this.generateId();
+ this.authProviders.put(id, authProvider);
+ return id;
+ }
+
+ private String generateId(){
+ Random r = new Random();
+ return Integer.toString(r.nextInt(1000+1));
+ }
+
+ private boolean getMetaDataFromManifest(String name) throws Exception{
+ return applicationContext.getPackageManager().getApplicationInfo(applicationContext.getPackageName(), PackageManager.GET_META_DATA).metaData.getBoolean(name);
+ }
+
+ private void setPreference(String name, boolean value){
+ SharedPreferences settings = cordovaActivity.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putBoolean(name, value);
+ editor.apply();
+ }
+
+ private boolean getPreference(String name){
+ SharedPreferences settings = cordovaActivity.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE);
+ return settings.getBoolean(name, false);
+ }
+
+ private void handleTaskOutcome(@NonNull Task<Void> task, CallbackContext callbackContext) {
+ try {
+ task.addOnCompleteListener(new OnCompleteListener<Void>() {
+ @Override
+ public void onComplete(@NonNull Task<Void> task) {
+ try {
+ if (task.isSuccessful() || task.getException() == null) {
+ callbackContext.success();
+ }else if(task.getException() != null){
+ callbackContext.error(task.getException().getMessage());
+ }else{
+ callbackContext.error("Task failed for unknown reason");
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ };
+ });
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+
+ private void handleAuthTaskOutcome(@NonNull Task<AuthResult> task, CallbackContext callbackContext) {
+ try {
+ if (task.isSuccessful() || task.getException() == null) {
+ callbackContext.success();
+ }else{
+ String errMessage = task.getException().getMessage();
+ if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
+ errMessage = "Invalid verification code";
+ }
+ callbackContext.error(errMessage);
+ }
+ } catch (Exception e) {
+ handleExceptionWithContext(e, callbackContext);
+ }
+ }
+
+ private AuthCredential obtainAuthCredential(JSONObject jsonCredential) throws JSONException {
+ AuthCredential authCredential = null;
+ if(jsonCredential.has("verificationId") && jsonCredential.has("code")){
+ Log.d(TAG, "Using specified verificationId and code to authenticate");
+ authCredential = (AuthCredential) PhoneAuthProvider.getCredential(jsonCredential.getString("verificationId"), jsonCredential.getString("code"));
+ }else if(jsonCredential.has("id") && FirebasePlugin.instance.authCredentials.containsKey(jsonCredential.getString("id"))){
+ Log.d(TAG, "Using native auth credential to authenticate");
+ authCredential = FirebasePlugin.instance.authCredentials.get(jsonCredential.getString("id"));
+ }
+ return authCredential;
+ }
+
+ private OAuthProvider obtainAuthProvider(JSONObject jsonCredential) throws JSONException{
+ OAuthProvider authProvider = null;
+ if(jsonCredential.has("id") && FirebasePlugin.instance.authProviders.containsKey(jsonCredential.getString("id"))){
+ Log.d(TAG, "Using native auth provider to authenticate");
+ authProvider = FirebasePlugin.instance.authProviders.get(jsonCredential.getString("id"));
+ }
+ return authProvider;
+ }
+
+
+ private static class AuthResultOnSuccessListener implements OnSuccessListener<AuthResult> {
+ @Override
+ public void onSuccess(AuthResult authResult) {
+ Log.d(TAG, "AuthResult:onSuccess:" + authResult);
+ if(FirebasePlugin.instance.authResultCallbackContext != null){
+ FirebasePlugin.instance.authResultCallbackContext.success();
+ }
+ }
+ }
+
+ private static class AuthResultOnFailureListener implements OnFailureListener {
+ @Override
+ public void onFailure(@NonNull Exception e) {
+ Log.w(TAG, "AuthResult:onFailure", e);
+ if(FirebasePlugin.instance.authResultCallbackContext != null){
+ FirebasePlugin.instance.authResultCallbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, e.getMessage()));
+ }
+ }
+ }
+
+ private static class AuthResultOnCompleteListener implements OnCompleteListener<AuthResult> {
+ private final CallbackContext callbackContext;
+
+ public AuthResultOnCompleteListener(CallbackContext callbackContext) {
+ this.callbackContext = callbackContext;
+ }
+
+ @Override
+ public void onComplete(@NonNull Task<AuthResult> task) {
+ FirebasePlugin.instance.handleAuthTaskOutcome(task, callbackContext);
+ }
+ }
+
+ private static class AuthStateListener implements FirebaseAuth.AuthStateListener {
+ @Override
+ public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
+ try {
+ if(!FirebasePlugin.instance.authStateChangeListenerInitialized){
+ FirebasePlugin.instance.authStateChangeListenerInitialized = true;
+ }else{
+ FirebaseUser user = firebaseAuth.getCurrentUser();
+ FirebasePlugin.instance.executeGlobalJavascript(JS_GLOBAL_NAMESPACE+"_onAuthStateChange("+(user != null ? "true" : "false")+")");
+ }
+ } catch (Exception e) {
+ handleExceptionWithoutContext(e);
+ }
+ }
+ }
+
+ private Map<String, Object> jsonStringToMap(String jsonString) throws JSONException {
+ Type type = new TypeToken<Map<String, Object>>(){}.getType();
+ return gson.fromJson(jsonString, type);
+ }
+
+
+ private JSONObject mapToJsonObject(Map<String, Object> map) throws JSONException {
+ String jsonString = gson.toJson(map);
+ return new JSONObject(jsonString);
+ }
+
+ private void logMessageToCrashlytics(String message){
+ if(isCrashlyticsEnabled()){
+ try{
+ firebaseCrashlytics.log(message);
+ }catch (Exception e){
+ Log.e(TAG, e.getMessage());
+ }
+ }else{
+ Log.e(TAG, "Cannot log message - Crashlytics collection is disabled");
+ }
+ }
+
+ private void logExceptionToCrashlytics(Exception exception){
+ if(isCrashlyticsEnabled()){
+ try{
+ firebaseCrashlytics.recordException(exception);
+ }catch (Exception e){
+ Log.e(TAG, e.getMessage());
+ }
+ }else{
+ Log.e(TAG, "Cannot log exception - Crashlytics collection is disabled");
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiver.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiver.java
new file mode 100644
index 00000000..1bb76d08
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiver.java
@@ -0,0 +1,28 @@
+package org.apache.cordova.firebase;
+
+import android.os.Bundle;
+
+import com.google.firebase.messaging.RemoteMessage;
+
+public abstract class FirebasePluginMessageReceiver {
+
+ public FirebasePluginMessageReceiver() {
+ FirebasePluginMessageReceiverManager.register(this);
+ }
+
+ /**
+ * Concrete subclasses should override this and return true if they handle the received message.
+ *
+ * @param remoteMessage
+ * @return true if the received message was handled by the receiver so should not be handled by FirebasePluginMessagingService.onMessageReceived()
+ */
+ public abstract boolean onMessageReceived(RemoteMessage remoteMessage);
+
+ /**
+ * Concrete subclasses should override this and return true if they handle the message bundle before it's sent to FirebasePlugin.sendMessage().
+ *
+ * @param bundle
+ * @return true if the received bundle was handled by the receiver so should not be handled by FirebasePlugin.
+ */
+ public abstract boolean sendMessage(Bundle bundle);
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiverManager.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiverManager.java
new file mode 100644
index 00000000..299e5cda
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessageReceiverManager.java
@@ -0,0 +1,41 @@
+package org.apache.cordova.firebase;
+
+import android.os.Bundle;
+
+import com.google.firebase.messaging.RemoteMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FirebasePluginMessageReceiverManager {
+
+ private static List<FirebasePluginMessageReceiver> receivers = new ArrayList<FirebasePluginMessageReceiver>();
+
+ public static void register(FirebasePluginMessageReceiver receiver) {
+ receivers.add(receiver);
+ }
+
+ public static boolean onMessageReceived(RemoteMessage remoteMessage) {
+ boolean handled = false;
+ for (FirebasePluginMessageReceiver receiver : receivers) {
+ boolean wasHandled = receiver.onMessageReceived(remoteMessage);
+ if (wasHandled) {
+ handled = true;
+ }
+ }
+
+ return handled;
+ }
+
+ public static boolean sendMessage(Bundle bundle) {
+ boolean handled = false;
+ for (FirebasePluginMessageReceiver receiver : receivers) {
+ boolean wasHandled = receiver.sendMessage(bundle);
+ if (wasHandled) {
+ handled = true;
+ }
+ }
+
+ return handled;
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessagingService.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessagingService.java
new file mode 100644
index 00000000..92e6aafb
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/FirebasePluginMessagingService.java
@@ -0,0 +1,348 @@
+package org.apache.cordova.firebase;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import androidx.core.app.NotificationCompat;
+import android.util.Log;
+import android.app.Notification;
+import android.text.TextUtils;
+import android.content.ContentResolver;
+import android.graphics.Color;
+
+import com.google.firebase.crashlytics.FirebaseCrashlytics;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+import java.util.Map;
+import java.util.Random;
+
+public class FirebasePluginMessagingService extends FirebaseMessagingService {
+
+ private static final String TAG = "FirebasePlugin";
+
+ static final String defaultSmallIconName = "notification_icon";
+ static final String defaultLargeIconName = "notification_icon_large";
+
+
+ /**
+ * Called if InstanceID token is updated. This may occur if the security of
+ * the previous token had been compromised. Note that this is called when the InstanceID token
+ * is initially generated so this is where you would retrieve the token.
+ */
+ @Override
+ public void onNewToken(String refreshedToken) {
+ try{
+ super.onNewToken(refreshedToken);
+ Log.d(TAG, "Refreshed token: " + refreshedToken);
+ FirebasePlugin.sendToken(refreshedToken);
+ }catch (Exception e){
+ FirebasePlugin.handleExceptionWithoutContext(e);
+ }
+ }
+
+
+ /**
+ * Called when message is received.
+ * Called IF message is a data message (i.e. NOT sent from Firebase console)
+ * OR if message is a notification message (e.g. sent from Firebase console) AND app is in foreground.
+ * Notification messages received while app is in background will not be processed by this method;
+ * they are handled internally by the OS.
+ *
+ * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
+ */
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ try{
+ // [START_EXCLUDE]
+ // There are two types of messages data messages and notification messages. Data messages are handled
+ // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
+ // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
+ // is in the foreground. When the app is in the background an automatically generated notification is displayed.
+ // When the user taps on the notification they are returned to the app. Messages containing both notification
+ // and data payloads are treated as notification messages. The Firebase console always sends notification
+ // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
+ // [END_EXCLUDE]
+
+ // Pass the message to the receiver manager so any registered receivers can decide to handle it
+ boolean wasHandled = FirebasePluginMessageReceiverManager.onMessageReceived(remoteMessage);
+ if (wasHandled) {
+ Log.d(TAG, "Message was handled by a registered receiver");
+
+ // Don't process the message in this method.
+ return;
+ }
+
+ if(FirebasePlugin.applicationContext == null){
+ FirebasePlugin.applicationContext = this.getApplicationContext();
+ }
+
+ // TODO(developer): Handle FCM messages here.
+ // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+ String messageType;
+ String title = null;
+ String body = null;
+ String id = null;
+ String sound = null;
+ String vibrate = null;
+ String light = null;
+ String color = null;
+ String icon = null;
+ String channelId = null;
+ String visibility = null;
+ String priority = null;
+ boolean foregroundNotification = false;
+
+ Map<String, String> data = remoteMessage.getData();
+
+ if (remoteMessage.getNotification() != null) {
+ // Notification message payload
+ Log.i(TAG, "Received message: notification");
+ messageType = "notification";
+ id = remoteMessage.getMessageId();
+ RemoteMessage.Notification notification = remoteMessage.getNotification();
+ title = notification.getTitle();
+ body = notification.getBody();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ channelId = notification.getChannelId();
+ }
+ sound = notification.getSound();
+ color = notification.getColor();
+ icon = notification.getIcon();
+ }else{
+ Log.i(TAG, "Received message: data");
+ messageType = "data";
+ }
+
+ if (data != null) {
+ // Data message payload
+ if(data.containsKey("notification_foreground")){
+ foregroundNotification = true;
+ }
+ if(data.containsKey("notification_title")) title = data.get("notification_title");
+ if(data.containsKey("notification_body")) body = data.get("notification_body");
+ if(data.containsKey("notification_android_channel_id")) channelId = data.get("notification_android_channel_id");
+ if(data.containsKey("notification_android_id")) id = data.get("notification_android_id");
+ if(data.containsKey("notification_android_sound")) sound = data.get("notification_android_sound");
+ if(data.containsKey("notification_android_vibrate")) vibrate = data.get("notification_android_vibrate");
+ if(data.containsKey("notification_android_light")) light = data.get("notification_android_light"); //String containing hex ARGB color, miliseconds on, miliseconds off, example: '#FFFF00FF,1000,3000'
+ if(data.containsKey("notification_android_color")) color = data.get("notification_android_color");
+ if(data.containsKey("notification_android_icon")) icon = data.get("notification_android_icon");
+ if(data.containsKey("notification_android_visibility")) visibility = data.get("notification_android_visibility");
+ if(data.containsKey("notification_android_priority")) priority = data.get("notification_android_priority");
+ }
+
+ if (TextUtils.isEmpty(id)) {
+ Random rand = new Random();
+ int n = rand.nextInt(50) + 1;
+ id = Integer.toString(n);
+ }
+
+ Log.d(TAG, "From: " + remoteMessage.getFrom());
+ Log.d(TAG, "Id: " + id);
+ Log.d(TAG, "Title: " + title);
+ Log.d(TAG, "Body: " + body);
+ Log.d(TAG, "Sound: " + sound);
+ Log.d(TAG, "Vibrate: " + vibrate);
+ Log.d(TAG, "Light: " + light);
+ Log.d(TAG, "Color: " + color);
+ Log.d(TAG, "Icon: " + icon);
+ Log.d(TAG, "Channel Id: " + channelId);
+ Log.d(TAG, "Visibility: " + visibility);
+ Log.d(TAG, "Priority: " + priority);
+
+
+ if (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title) || (data != null && !data.isEmpty())) {
+ boolean showNotification = (FirebasePlugin.inBackground() || !FirebasePlugin.hasNotificationsCallback() || foregroundNotification) && (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title));
+ sendMessage(remoteMessage, data, messageType, id, title, body, showNotification, sound, vibrate, light, color, icon, channelId, priority, visibility);
+ }
+ }catch (Exception e){
+ FirebasePlugin.handleExceptionWithoutContext(e);
+ }
+ }
+
+ private void sendMessage(RemoteMessage remoteMessage, Map<String, String> data, String messageType, String id, String title, String body, boolean showNotification, String sound, String vibrate, String light, String color, String icon, String channelId, String priority, String visibility) {
+ Log.d(TAG, "sendMessage(): messageType="+messageType+"; showNotification="+showNotification+"; id="+id+"; title="+title+"; body="+body+"; sound="+sound+"; vibrate="+vibrate+"; light="+light+"; color="+color+"; icon="+icon+"; channel="+channelId+"; data="+data.toString());
+ Bundle bundle = new Bundle();
+ for (String key : data.keySet()) {
+ bundle.putString(key, data.get(key));
+ }
+ bundle.putString("messageType", messageType);
+ this.putKVInBundle("id", id, bundle);
+ this.putKVInBundle("title", title, bundle);
+ this.putKVInBundle("body", body, bundle);
+ this.putKVInBundle("sound", sound, bundle);
+ this.putKVInBundle("vibrate", vibrate, bundle);
+ this.putKVInBundle("light", light, bundle);
+ this.putKVInBundle("color", color, bundle);
+ this.putKVInBundle("icon", icon, bundle);
+ this.putKVInBundle("channel_id", channelId, bundle);
+ this.putKVInBundle("priority", priority, bundle);
+ this.putKVInBundle("visibility", visibility, bundle);
+ this.putKVInBundle("show_notification", String.valueOf(showNotification), bundle);
+ this.putKVInBundle("from", remoteMessage.getFrom(), bundle);
+ this.putKVInBundle("collapse_key", remoteMessage.getCollapseKey(), bundle);
+ this.putKVInBundle("sent_time", String.valueOf(remoteMessage.getSentTime()), bundle);
+ this.putKVInBundle("ttl", String.valueOf(remoteMessage.getTtl()), bundle);
+
+ if (showNotification) {
+ Intent intent = new Intent(this, OnNotificationOpenReceiver.class);
+ intent.putExtras(bundle);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, id.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // Channel
+ if(channelId == null || !FirebasePlugin.channelExists(channelId)){
+ channelId = FirebasePlugin.defaultChannelId;
+ }
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
+ Log.d(TAG, "Channel ID: "+channelId);
+ }
+
+ NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
+ notificationBuilder
+ .setContentTitle(title)
+ .setContentText(body)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(body))
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent);
+
+ // On Android O+ the sound/lights/vibration are determined by the channel ID
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
+ // Sound
+ if (sound == null) {
+ Log.d(TAG, "Sound: none");
+ }else if (sound.equals("default")) {
+ notificationBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
+ Log.d(TAG, "Sound: default");
+ }else{
+ Uri soundPath = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/raw/" + sound);
+ Log.d(TAG, "Sound: custom=" + sound+"; path="+soundPath.toString());
+ notificationBuilder.setSound(soundPath);
+ }
+
+ // Light
+ if (light != null) {
+ try {
+ String[] lightsComponents = color.replaceAll("\\s", "").split(",");
+ if (lightsComponents.length == 3) {
+ int lightArgb = Color.parseColor(lightsComponents[0]);
+ int lightOnMs = Integer.parseInt(lightsComponents[1]);
+ int lightOffMs = Integer.parseInt(lightsComponents[2]);
+ notificationBuilder.setLights(lightArgb, lightOnMs, lightOffMs);
+ Log.d(TAG, "Lights: color="+lightsComponents[0]+"; on(ms)="+lightsComponents[2]+"; off(ms)="+lightsComponents[3]);
+ }
+
+ } catch (Exception e) {}
+ }
+
+ // Vibrate
+ if (vibrate != null){
+ try {
+ String[] sVibrations = vibrate.replaceAll("\\s", "").split(",");
+ long[] lVibrations = new long[sVibrations.length];
+ int i=0;
+ for(String sVibration: sVibrations){
+ lVibrations[i] = Integer.parseInt(sVibration.trim());
+ i++;
+ }
+ notificationBuilder.setVibrate(lVibrations);
+ Log.d(TAG, "Vibrate: "+vibrate);
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+ }
+
+
+ // Icon
+ int defaultSmallIconResID = getResources().getIdentifier(defaultSmallIconName, "drawable", getPackageName());
+ int customSmallIconResID = 0;
+ if(icon != null){
+ customSmallIconResID = getResources().getIdentifier(icon, "drawable", getPackageName());
+ }
+
+ if (customSmallIconResID != 0) {
+ notificationBuilder.setSmallIcon(customSmallIconResID);
+ Log.d(TAG, "Small icon: custom="+icon);
+ }else if (defaultSmallIconResID != 0) {
+ Log.d(TAG, "Small icon: default="+defaultSmallIconName);
+ notificationBuilder.setSmallIcon(defaultSmallIconResID);
+ } else {
+ Log.d(TAG, "Small icon: application");
+ notificationBuilder.setSmallIcon(getApplicationInfo().icon);
+ }
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
+ int defaultLargeIconResID = getResources().getIdentifier(defaultLargeIconName, "drawable", getPackageName());
+ int customLargeIconResID = 0;
+ if(icon != null){
+ customLargeIconResID = getResources().getIdentifier(icon+"_large", "drawable", getPackageName());
+ }
+
+ int largeIconResID;
+ if (customLargeIconResID != 0 || defaultLargeIconResID != 0) {
+ if (customLargeIconResID != 0) {
+ largeIconResID = customLargeIconResID;
+ Log.d(TAG, "Large icon: custom="+icon);
+ }else{
+ Log.d(TAG, "Large icon: default="+defaultLargeIconName);
+ largeIconResID = defaultLargeIconResID;
+ }
+ notificationBuilder.setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), largeIconResID));
+ }
+ }
+
+ // Color
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ int defaultColor = getResources().getColor(getResources().getIdentifier("accent", "color", getPackageName()), null);
+ if(color != null){
+ notificationBuilder.setColor(Color.parseColor(color));
+ Log.d(TAG, "Color: custom="+color);
+ }else{
+ Log.d(TAG, "Color: default");
+ notificationBuilder.setColor(defaultColor);
+ }
+ }
+
+ // Visibility
+ int iVisibility = NotificationCompat.VISIBILITY_PUBLIC;
+ if(visibility != null){
+ iVisibility = Integer.parseInt(visibility);
+ }
+ Log.d(TAG, "Visibility: " + iVisibility);
+ notificationBuilder.setVisibility(iVisibility);
+
+ // Priority
+ int iPriority = NotificationCompat.PRIORITY_MAX;
+ if(priority != null){
+ iPriority = Integer.parseInt(priority);
+ }
+ Log.d(TAG, "Priority: " + iPriority);
+ notificationBuilder.setPriority(iPriority);
+
+
+ // Build notification
+ Notification notification = notificationBuilder.build();
+
+ // Display notification
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ Log.d(TAG, "show notification: "+notification.toString());
+ notificationManager.notify(id.hashCode(), notification);
+ }
+ // Send to plugin
+ FirebasePlugin.sendMessage(bundle, this.getApplicationContext());
+ }
+
+ private void putKVInBundle(String k, String v, Bundle b){
+ if(v != null && !b.containsKey(k)){
+ b.putString(k, v);
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/JavaScriptException.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/JavaScriptException.java
new file mode 100644
index 00000000..3d423fa3
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/JavaScriptException.java
@@ -0,0 +1,45 @@
+package org.apache.cordova.firebase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Exception class to log Javascript based exceptions with stacktrace.
+ *
+ * Picked from https://github.com/wizpanda/cordova-plugin-firebase-lib/pull/8/files
+ *
+ * @author https://github.com/sagrawal31/
+ */
+public class JavaScriptException extends Exception {
+
+ public JavaScriptException(String message) {
+ super(message);
+ }
+
+ public JavaScriptException(String message, JSONArray stackTrace) throws JSONException {
+ super(message);
+ this.handleStacktrace(stackTrace);
+ }
+
+ private void handleStacktrace(JSONArray stackTrace) throws JSONException {
+ if (stackTrace == null) {
+ return;
+ }
+
+ StackTraceElement[] trace = new StackTraceElement[stackTrace.length()];
+
+ for (int i = 0; i < stackTrace.length(); i++) {
+ JSONObject elem = stackTrace.getJSONObject(i);
+
+ trace[i] = new StackTraceElement(
+ "undefined",
+ elem.optString("functionName", "undefined"),
+ elem.optString("fileName", "undefined"),
+ elem.optInt("lineNumber", -1)
+ );
+ }
+
+ this.setStackTrace(trace);
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/OnNotificationOpenReceiver.java b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/OnNotificationOpenReceiver.java
new file mode 100644
index 00000000..818382ef
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/OnNotificationOpenReceiver.java
@@ -0,0 +1,36 @@
+package org.apache.cordova.firebase;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+
+public class OnNotificationOpenReceiver extends BroadcastReceiver {
+
+ // Called on tapping foreground notification
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try{
+ PackageManager pm = context.getPackageManager();
+
+ Intent launchIntent = pm.getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Bundle data = intent.getExtras();
+ if(!data.containsKey("messageType")) data.putString("messageType", "notification");
+ data.putString("tap", FirebasePlugin.inBackground() ? "background" : "foreground");
+
+ Log.d(FirebasePlugin.TAG, "OnNotificationOpenReceiver.onReceive(): "+data.toString());
+
+ FirebasePlugin.sendMessage(data, context);
+
+ launchIntent.putExtras(data);
+ context.startActivity(launchIntent);
+ }catch (Exception e){
+ FirebasePlugin.handleExceptionWithoutContext(e);
+ }
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/build.gradle b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/build.gradle
new file mode 100644
index 00000000..89c743f1
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/build.gradle
@@ -0,0 +1,48 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.google.gms:google-services:4.3.3'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
+ }
+}
+repositories {
+ mavenCentral()
+ maven {
+ url "https://maven.google.com"
+ }
+}
+
+apply plugin: com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin
+android {
+ buildTypes {
+ debug {
+ firebaseCrashlytics {
+ mappingFileUploadEnabled false
+ }
+ }
+ release {
+ firebaseCrashlytics {
+ nativeSymbolUploadEnabled true
+ unstrippedNativeLibsDir "obj/local"
+ strippedNativeLibsDir "build/intermediates/jniLibs/release"
+ }
+ }
+ }
+}
+
+cdvPluginPostBuildExtras.add({
+ rootProject.subprojects {
+ if (name == "app") {
+ if (!plugins.hasPlugin('com.google.gms.google-services')) {
+ apply plugin: com.google.gms.googleservices.GoogleServicesPlugin
+ }
+ }
+ }
+})
+
+
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/colors.xml b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/colors.xml
new file mode 100644
index 00000000..045e125f
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/colors.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+</resources>
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/android/cordova-plugin-firebase-strings.xml b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/cordova-plugin-firebase-strings.xml
new file mode 100644
index 00000000..1f1d32cc
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/android/cordova-plugin-firebase-strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="default_notification_channel_id">fcm_default_channel</string>
+ <string name="default_notification_channel_name">Default</string>
+</resources> \ No newline at end of file
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.h b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.h
new file mode 100644
index 00000000..461b17e4
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.h
@@ -0,0 +1,11 @@
+#import "AppDelegate.h"
+#import <GoogleSignIn/GoogleSignIn.h>
+
+@import UserNotifications;
+@import AuthenticationServices;
+
+@interface AppDelegate (FirebasePlugin) <UIApplicationDelegate, GIDSignInDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
++ (AppDelegate *) instance;
+@property (nonatomic, strong) NSNumber * _Nonnull applicationInBackground;
+@property (NS_NONATOMIC_IOSONLY, nullable, weak) id <UNUserNotificationCenterDelegate> delegate;
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.m b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.m
new file mode 100644
index 00000000..a14c6569
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/AppDelegate+FirebasePlugin.m
@@ -0,0 +1,557 @@
+#import "AppDelegate+FirebasePlugin.h"
+#import "FirebasePlugin.h"
+#import "Firebase.h"
+#import <objc/runtime.h>
+
+
+@import UserNotifications;
+@import FirebaseFirestore;
+
+// Implement UNUserNotificationCenterDelegate to receive display notification via APNS for devices running iOS 10 and above.
+// Implement FIRMessagingDelegate to receive data message via FCM for devices running iOS 10 and above.
+@interface AppDelegate () <UNUserNotificationCenterDelegate, FIRMessagingDelegate>
+@end
+
+#define kApplicationInBackgroundKey @"applicationInBackground"
+
+@implementation AppDelegate (FirebasePlugin)
+
+static AppDelegate* instance;
+
++ (AppDelegate*) instance {
+ return instance;
+}
+
+static NSDictionary* mutableUserInfo;
+static FIRAuthStateDidChangeListenerHandle authStateChangeListener;
+static bool authStateChangeListenerInitialized = false;
+static bool shouldEstablishDirectChannel = false;
+
++ (void)load {
+ Method original = class_getInstanceMethod(self, @selector(application:didFinishLaunchingWithOptions:));
+ Method swizzled = class_getInstanceMethod(self, @selector(application:swizzledDidFinishLaunchingWithOptions:));
+ method_exchangeImplementations(original, swizzled);
+}
+
+- (void)setApplicationInBackground:(NSNumber *)applicationInBackground {
+ objc_setAssociatedObject(self, kApplicationInBackgroundKey, applicationInBackground, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (NSNumber *)applicationInBackground {
+ return objc_getAssociatedObject(self, kApplicationInBackgroundKey);
+}
+
+- (BOOL)application:(UIApplication *)application swizzledDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ [self application:application swizzledDidFinishLaunchingWithOptions:launchOptions];
+
+ @try{
+ instance = self;
+
+ bool isFirebaseInitializedWithPlist = false;
+ if(![FIRApp defaultApp]) {
+ // get GoogleService-Info.plist file path
+ NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
+
+ // if file is successfully found, use it
+ if(filePath){
+ [FirebasePlugin.firebasePlugin _logMessage:@"GoogleService-Info.plist found, setup: [FIRApp configureWithOptions]"];
+ // create firebase configure options passing .plist as content
+ FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
+
+ // configure FIRApp with options
+ [FIRApp configureWithOptions:options];
+
+ isFirebaseInitializedWithPlist = true;
+ }else{
+ // no .plist found, try default App
+ [FirebasePlugin.firebasePlugin _logError:@"GoogleService-Info.plist NOT FOUND, setup: [FIRApp defaultApp]"];
+ [FIRApp configure];
+ }
+ }else{
+ // Firebase SDK has already been initialised:
+ // Assume that another call (probably from another plugin) did so with the plist
+ isFirebaseInitializedWithPlist = true;
+ }
+
+
+
+ shouldEstablishDirectChannel = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"shouldEstablishDirectChannel"] boolValue];
+
+ // Set FCM messaging delegate
+ [FIRMessaging messaging].delegate = self;
+ [FIRMessaging messaging].shouldEstablishDirectChannel = shouldEstablishDirectChannel;
+
+ // Setup Firestore
+ [FirebasePlugin setFirestore:[FIRFirestore firestore]];
+
+ // Setup Google SignIn
+ [GIDSignIn sharedInstance].clientID = [FIRApp defaultApp].options.clientID;
+ [GIDSignIn sharedInstance].delegate = self;
+
+ authStateChangeListener = [[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
+ @try {
+ if(!authStateChangeListenerInitialized){
+ authStateChangeListenerInitialized = true;
+ }else{
+ [FirebasePlugin.firebasePlugin executeGlobalJavascript:[NSString stringWithFormat:@"FirebasePlugin._onAuthStateChange(%@)", (user != nil ? @"true": @"false")]];
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+ }];
+
+ // Set NSNotificationCenter observer
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tokenRefreshNotification:)
+ name:kFIRInstanceIDTokenRefreshNotification object:nil];
+
+ self.applicationInBackground = @(YES);
+
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+
+ return YES;
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ self.applicationInBackground = @(NO);
+ [FIRMessaging messaging].shouldEstablishDirectChannel = shouldEstablishDirectChannel;
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"Enter foreground: FCM direct channel = %@", shouldEstablishDirectChannel ? @"true" : @"false"]];
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ self.applicationInBackground = @(YES);
+ [FIRMessaging messaging].shouldEstablishDirectChannel = false;
+ [FirebasePlugin.firebasePlugin _logMessage:@"Enter background: FCM direct channel = false"];
+}
+
+# pragma mark - Google SignIn
+- (void)signIn:(GIDSignIn *)signIn
+didSignInForUser:(GIDGoogleUser *)user
+ withError:(NSError *)error {
+ @try{
+ CDVPluginResult* pluginResult;
+ if (error == nil) {
+ GIDAuthentication *authentication = user.authentication;
+ FIRAuthCredential *credential =
+ [FIRGoogleAuthProvider credentialWithIDToken:authentication.idToken
+ accessToken:authentication.accessToken];
+
+ int key = [[FirebasePlugin firebasePlugin] saveAuthCredential:credential];
+ NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
+ [result setValue:@"true" forKey:@"instantVerification"];
+ [result setValue:[NSNumber numberWithInt:key] forKey:@"id"];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description];
+ }
+ if ([FirebasePlugin firebasePlugin].googleSignInCallbackId != nil) {
+ [[FirebasePlugin firebasePlugin].commandDelegate sendPluginResult:pluginResult callbackId:[FirebasePlugin firebasePlugin].googleSignInCallbackId];
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+- (void)signIn:(GIDSignIn *)signIn
+didDisconnectWithUser:(GIDGoogleUser *)user
+ withError:(NSError *)error {
+ NSString* msg = @"Google SignIn delegate: didDisconnectWithUser";
+ if(error != nil){
+ [FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:@"%@: %@", msg, error]];
+ }else{
+ [FirebasePlugin.firebasePlugin _logMessage:msg];
+ }
+}
+
+# pragma mark - FIRMessagingDelegate
+- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"didReceiveRegistrationToken: %@", fcmToken]];
+ @try{
+ [FirebasePlugin.firebasePlugin sendToken:fcmToken];
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+- (void)tokenRefreshNotification:(NSNotification *)notification {
+ // Note that this callback will be fired everytime a new token is generated, including the first
+ // time. So if you need to retrieve the token as soon as it is available this is where that
+ // should be done.
+ @try{
+ [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
+ NSError * _Nullable error) {
+ @try{
+ if (error == nil) {
+ NSString *refreshedToken = result.token;
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"tokenRefreshNotification: %@", refreshedToken]];
+ [FirebasePlugin.firebasePlugin sendToken:refreshedToken];
+ }else{
+ [FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:@"tokenRefreshNotification: %@", error.description]];
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+ [FIRMessaging messaging].APNSToken = deviceToken;
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"didRegisterForRemoteNotificationsWithDeviceToken: %@", deviceToken]];
+ [FirebasePlugin.firebasePlugin sendApnsToken:[FirebasePlugin.firebasePlugin hexadecimalStringFromData:deviceToken]];
+
+ // Set UNUserNotificationCenter delegate
+ [UNUserNotificationCenter currentNotificationCenter].delegate = self;
+}
+
+//Tells the app that a remote notification arrived that indicates there is data to be fetched.
+// Called when a message arrives in the foreground and remote notifications permission has been granted
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
+ fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+
+ @try{
+ [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
+ mutableUserInfo = [userInfo mutableCopy];
+ NSDictionary* aps = [mutableUserInfo objectForKey:@"aps"];
+ bool isContentAvailable = false;
+ if([aps objectForKey:@"alert"] != nil){
+ isContentAvailable = [[aps objectForKey:@"content-available"] isEqualToNumber:[NSNumber numberWithInt:1]];
+ [mutableUserInfo setValue:@"notification" forKey:@"messageType"];
+ NSString* tap;
+ if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] && !isContentAvailable){
+ tap = @"background";
+ }
+ [mutableUserInfo setValue:tap forKey:@"tap"];
+ }else{
+ [mutableUserInfo setValue:@"data" forKey:@"messageType"];
+ }
+
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"didReceiveRemoteNotification: %@", mutableUserInfo]];
+
+ completionHandler(UIBackgroundFetchResultNewData);
+ if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] && isContentAvailable){
+ [FirebasePlugin.firebasePlugin _logError:@"didReceiveRemoteNotification: omitting foreground notification as content-available:1 so system notification will be shown"];
+ }else{
+ [self processMessageForForegroundNotification:mutableUserInfo];
+ }
+ if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] || !isContentAvailable){
+ [FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
+// Called when a data message is arrives in the foreground and remote notifications permission has been NOT been granted
+- (void)messaging:(FIRMessaging *)messaging didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
+ @try{
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"didReceiveMessage: %@", remoteMessage.appData]];
+
+ NSDictionary* appData = [remoteMessage.appData mutableCopy];
+ [appData setValue:@"data" forKey:@"messageType"];
+ [self processMessageForForegroundNotification:appData];
+
+ // This will allow us to handle FCM data-only push messages even if the permission for push
+ // notifications is yet missing. This will only work when the app is in the foreground.
+ [FirebasePlugin.firebasePlugin sendNotification:appData];
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+// Scans a message for keys which indicate a notification should be shown.
+// If found, extracts relevant keys and uses then to display a local notification
+-(void)processMessageForForegroundNotification:(NSDictionary*)messageData {
+ bool showForegroundNotification = [messageData objectForKey:@"notification_foreground"];
+ if(!showForegroundNotification){
+ return;
+ }
+
+ NSString* title = nil;
+ NSString* body = nil;
+ NSString* sound = nil;
+ NSNumber* badge = nil;
+
+ // Extract APNS notification keys
+ NSDictionary* aps = [messageData objectForKey:@"aps"];
+ if([aps objectForKey:@"alert"] != nil){
+ NSDictionary* alert = [aps objectForKey:@"alert"];
+ if([alert objectForKey:@"title"] != nil){
+ title = [alert objectForKey:@"title"];
+ }
+ if([alert objectForKey:@"body"] != nil){
+ body = [alert objectForKey:@"body"];
+ }
+ if([aps objectForKey:@"sound"] != nil){
+ sound = [aps objectForKey:@"sound"];
+ }
+ if([aps objectForKey:@"badge"] != nil){
+ badge = [aps objectForKey:@"badge"];
+ }
+ }
+
+ // Extract data notification keys
+ if([messageData objectForKey:@"notification_title"] != nil){
+ title = [messageData objectForKey:@"notification_title"];
+ }
+ if([messageData objectForKey:@"notification_body"] != nil){
+ body = [messageData objectForKey:@"notification_body"];
+ }
+ if([messageData objectForKey:@"notification_ios_sound"] != nil){
+ sound = [messageData objectForKey:@"notification_ios_sound"];
+ }
+ if([messageData objectForKey:@"notification_ios_badge"] != nil){
+ badge = [messageData objectForKey:@"notification_ios_badge"];
+ }
+
+ if(title == nil || body == nil){
+ return;
+ }
+
+ [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
+ @try{
+ if (settings.alertSetting == UNNotificationSettingEnabled) {
+ UNMutableNotificationContent *objNotificationContent = [[UNMutableNotificationContent alloc] init];
+ objNotificationContent.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
+ objNotificationContent.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
+
+ NSDictionary* alert = [[NSDictionary alloc] initWithObjectsAndKeys:
+ title, @"title",
+ body, @"body"
+ , nil];
+ NSMutableDictionary* aps = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ alert, @"alert",
+ nil];
+
+ if(![sound isKindOfClass:[NSString class]] || [sound isEqualToString:@"default"]){
+ objNotificationContent.sound = [UNNotificationSound defaultSound];
+ [aps setValue:sound forKey:@"sound"];
+ }else if(sound != nil){
+ objNotificationContent.sound = [UNNotificationSound soundNamed:sound];
+ [aps setValue:sound forKey:@"sound"];
+ }
+
+ if(badge != nil){
+ [aps setValue:badge forKey:@"badge"];
+ }
+
+ NSString* messageType = @"data";
+ if([mutableUserInfo objectForKey:@"messageType"] != nil){
+ messageType = [mutableUserInfo objectForKey:@"messageType"];
+ }
+
+ NSDictionary* userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"true", @"notification_foreground",
+ messageType, @"messageType",
+ aps, @"aps"
+ , nil];
+
+ objNotificationContent.userInfo = userInfo;
+
+ UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1f repeats:NO];
+ UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"local_notification" content:objNotificationContent trigger:trigger];
+ [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
+ if (!error) {
+ [FirebasePlugin.firebasePlugin _logMessage:@"Local Notification succeeded"];
+ } else {
+ [FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:@"Local Notification failed: %@", error.description]];
+ }
+ }];
+ }else{
+ [FirebasePlugin.firebasePlugin _logError:@"processMessageForForegroundNotification: cannot show notification as permission denied"];
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+ }];
+}
+
+- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
+ [FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:@"didFailToRegisterForRemoteNotificationsWithError: %@", error.description]];
+}
+
+// Asks the delegate how to handle a notification that arrived while the app was running in the foreground
+// Called when an APS notification arrives when app is in foreground
+- (void)userNotificationCenter:(UNUserNotificationCenter *)center
+ willPresentNotification:(UNNotification *)notification
+ withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
+
+ @try{
+
+ if (![notification.request.trigger isKindOfClass:UNPushNotificationTrigger.class] && ![notification.request.trigger isKindOfClass:UNTimeIntervalNotificationTrigger.class]){
+ [FirebasePlugin.firebasePlugin _logError:@"willPresentNotification: aborting as not a supported UNNotificationTrigger"];
+ return;
+ }
+
+ [[FIRMessaging messaging] appDidReceiveMessage:notification.request.content.userInfo];
+
+ mutableUserInfo = [notification.request.content.userInfo mutableCopy];
+
+ NSString* messageType = [mutableUserInfo objectForKey:@"messageType"];
+ if(![messageType isEqualToString:@"data"]){
+ [mutableUserInfo setValue:@"notification" forKey:@"messageType"];
+ }
+
+ // Print full message.
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"willPresentNotification: %@", mutableUserInfo]];
+
+
+ NSDictionary* aps = [mutableUserInfo objectForKey:@"aps"];
+ bool isContentAvailable = [[aps objectForKey:@"content-available"] isEqualToNumber:[NSNumber numberWithInt:1]];
+ if(isContentAvailable){
+ [FirebasePlugin.firebasePlugin _logError:@"willPresentNotification: aborting as content-available:1 so system notification will be shown"];
+ return;
+ }
+
+ bool showForegroundNotification = [mutableUserInfo objectForKey:@"notification_foreground"];
+ bool hasAlert = [aps objectForKey:@"alert"] != nil;
+ bool hasBadge = [aps objectForKey:@"badge"] != nil;
+ bool hasSound = [aps objectForKey:@"sound"] != nil;
+
+ if(showForegroundNotification){
+ [FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:@"willPresentNotification: foreground notification alert=%@, badge=%@, sound=%@", hasAlert ? @"YES" : @"NO", hasBadge ? @"YES" : @"NO", hasSound ? @"YES" : @"NO"]];
+ if(hasAlert && hasBadge && hasSound){
+ completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionBadge + UNNotificationPresentationOptionSound);
+ }else if(hasAlert && hasBadge){
+ completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionBadge);
+ }else if(hasAlert && hasSound){
+ completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
+ }else if(hasBadge && hasSound){
+ completionHandler(UNNotificationPresentationOptionBadge + UNNotificationPresentationOptionSound);
+ }else if(hasAlert){
+ completionHandler(UNNotificationPresentationOptionAlert);
+ }else if(hasBadge){
+ completionHandler(UNNotificationPresentationOptionBadge);
+ }else if(hasSound){
+ completionHandler(UNNotificationPresentationOptionSound);
+ }
+ }else{
+ [FirebasePlugin.firebasePlugin _logMessage:@"willPresentNotification: foreground notification not set"];
+ }
+
+ if(![messageType isEqualToString:@"data"]){
+ [FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
+ }
+
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+// Asks the delegate to process the user's response to a delivered notification.
+// Called when user taps on system notification
+- (void) userNotificationCenter:(UNUserNotificationCenter *)center
+ didReceiveNotificationResponse:(UNNotificationResponse *)response
+ withCompletionHandler:(void (^)(void))completionHandler
+{
+ @try{
+
+ if (![response.notification.request.trigger isKindOfClass:UNPushNotificationTrigger.class] && ![response.notification.request.trigger isKindOfClass:UNTimeIntervalNotificationTrigger.class]){
+ [FirebasePlugin.firebasePlugin _logMessage:@"didReceiveNotificationResponse: aborting as not a supported UNNotificationTrigger"];
+ return;
+ }
+
+ [[FIRMessaging messaging] appDidReceiveMessage:response.notification.request.content.userInfo];
+
+ mutableUserInfo = [response.notification.request.content.userInfo mutableCopy];
+
+ NSString* tap;
+ if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]]){
+ tap = @"background";
+ }else{
+ tap = @"foreground";
+
+ }
+ [mutableUserInfo setValue:tap forKey:@"tap"];
+ if([mutableUserInfo objectForKey:@"messageType"] == nil){
+ [mutableUserInfo setValue:@"notification" forKey:@"messageType"];
+ }
+
+ // Dynamic Actions
+ if (response.actionIdentifier && ![response.actionIdentifier isEqual:UNNotificationDefaultActionIdentifier]) {
+ [mutableUserInfo setValue:response.actionIdentifier forKey:@"action"];
+ }
+
+ // Print full message.
+ [FirebasePlugin.firebasePlugin _logInfo:[NSString stringWithFormat:@"didReceiveNotificationResponse: %@", mutableUserInfo]];
+
+ [FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
+
+ completionHandler();
+
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+// Receive data message on iOS 10 devices.
+- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
+ // Print full message
+ [FirebasePlugin.firebasePlugin _logInfo:[NSString stringWithFormat:@"applicationReceivedRemoteMessage: %@", [remoteMessage appData]]];
+}
+
+// Apple Sign In
+- (void)authorizationController:(ASAuthorizationController *)controller
+ didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
+ @try{
+ CDVPluginResult* pluginResult;
+ NSString* errorMessage = nil;
+ FIROAuthCredential *credential;
+
+ if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
+ ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
+ NSString *rawNonce = [FirebasePlugin appleSignInNonce];
+ if(rawNonce == nil){
+ errorMessage = @"Invalid state: A login callback was received, but no login request was sent.";
+ }else if (appleIDCredential.identityToken == nil) {
+ errorMessage = @"Unable to fetch identity token.";
+ }else{
+ NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken
+ encoding:NSUTF8StringEncoding];
+ if (idToken == nil) {
+ errorMessage = [NSString stringWithFormat:@"Unable to serialize id token from data: %@", appleIDCredential.identityToken];
+ }else{
+ // Initialize a Firebase credential.
+ credential = [FIROAuthProvider credentialWithProviderID:@"apple.com"
+ IDToken:idToken
+ rawNonce:rawNonce];
+
+ int key = [[FirebasePlugin firebasePlugin] saveAuthCredential:credential];
+ NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
+ [result setValue:@"true" forKey:@"instantVerification"];
+ [result setValue:[NSNumber numberWithInt:key] forKey:@"id"];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
+ }
+ }
+ if(errorMessage != nil){
+ [FirebasePlugin.firebasePlugin _logError:errorMessage];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage];
+ }
+ if ([FirebasePlugin firebasePlugin].appleSignInCallbackId != nil) {
+ [[FirebasePlugin firebasePlugin].commandDelegate sendPluginResult:pluginResult callbackId:[FirebasePlugin firebasePlugin].appleSignInCallbackId];
+ }
+ }
+ }@catch (NSException *exception) {
+ [FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+- (void)authorizationController:(ASAuthorizationController *)controller
+ didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
+ NSString* errorMessage = [NSString stringWithFormat:@"Sign in with Apple errored: %@", error];
+ [FirebasePlugin.firebasePlugin _logError:errorMessage];
+ if ([FirebasePlugin firebasePlugin].appleSignInCallbackId != nil) {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage];
+ [[FirebasePlugin firebasePlugin].commandDelegate sendPluginResult:pluginResult callbackId:[FirebasePlugin firebasePlugin].appleSignInCallbackId];
+ }
+}
+
+- (nonnull ASPresentationAnchor)presentationAnchorForAuthorizationController:(nonnull ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)){
+ return self.viewController.view.window;
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.h b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.h
new file mode 100644
index 00000000..2a65108d
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.h
@@ -0,0 +1,119 @@
+#import <Cordova/CDV.h>
+#import "AppDelegate.h"
+#import "Firebase.h"
+@import FirebaseFirestore;
+
+@interface FirebasePlugin : CDVPlugin
+
+- (void)setAutoInitEnabled:(CDVInvokedUrlCommand*)command;
+- (void)isAutoInitEnabled:(CDVInvokedUrlCommand*)command;
+
+// Authentication
+- (void)verifyPhoneNumber:(CDVInvokedUrlCommand*)command;
+- (void)createUserWithEmailAndPassword:(CDVInvokedUrlCommand*)command;
+- (void)signInUserWithEmailAndPassword:(CDVInvokedUrlCommand*)command;
+- (void)signInUserWithCustomToken:(CDVInvokedUrlCommand*)command;
+- (void)signInUserAnonymously:(CDVInvokedUrlCommand*)command;
+- (void)authenticateUserWithGoogle:(CDVInvokedUrlCommand*)command;
+- (void)authenticateUserWithApple:(CDVInvokedUrlCommand*)command;
+- (void)signInWithCredential:(CDVInvokedUrlCommand*)command;
+- (void)linkUserWithCredential:(CDVInvokedUrlCommand*)command;
+- (void)reauthenticateWithCredential:(CDVInvokedUrlCommand*)command;
+- (void)isUserSignedIn:(CDVInvokedUrlCommand*)command;
+- (void)signOutUser:(CDVInvokedUrlCommand*)command;
+- (void)getCurrentUser:(CDVInvokedUrlCommand*)command;
+- (void)reloadCurrentUser:(CDVInvokedUrlCommand*)command;
+- (void)updateUserProfile:(CDVInvokedUrlCommand*)command;
+- (void)updateUserEmail:(CDVInvokedUrlCommand*)command;
+- (void)sendUserEmailVerification:(CDVInvokedUrlCommand*)command;
+- (void)updateUserPassword:(CDVInvokedUrlCommand*)command;
+- (void)sendUserPasswordResetEmail:(CDVInvokedUrlCommand*)command;
+- (void)deleteUser:(CDVInvokedUrlCommand*)command;
+
+// Remote notifications
+- (void)getId:(CDVInvokedUrlCommand*)command;
+- (void)getToken:(CDVInvokedUrlCommand*)command;
+- (void)getAPNSToken:(CDVInvokedUrlCommand*)command;
+- (NSString *)hexadecimalStringFromData:(NSData *)data;
+- (void)grantPermission:(CDVInvokedUrlCommand*)command;
+- (void)hasPermission:(CDVInvokedUrlCommand*)command;
+- (void)setBadgeNumber:(CDVInvokedUrlCommand*)command;
+- (void)getBadgeNumber:(CDVInvokedUrlCommand*)command;
+- (void)subscribe:(CDVInvokedUrlCommand*)command;
+- (void)unsubscribe:(CDVInvokedUrlCommand*)command;
+- (void)unregister:(CDVInvokedUrlCommand*)command;
+- (void)onMessageReceived:(CDVInvokedUrlCommand*)command;
+- (void)onTokenRefresh:(CDVInvokedUrlCommand*)command;
+- (void)onApnsTokenReceived:(CDVInvokedUrlCommand *)command;
+- (void)sendNotification:(NSDictionary*)userInfo;
+- (void)sendToken:(NSString*)token;
+- (void)sendApnsToken:(NSString*)token;
+- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command;
+
+// Analytics
+- (void)setAnalyticsCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)isAnalyticsCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)logEvent:(CDVInvokedUrlCommand*)command;
+- (void)setScreenName:(CDVInvokedUrlCommand*)command;
+- (void)setUserId:(CDVInvokedUrlCommand*)command;
+- (void)setUserProperty:(CDVInvokedUrlCommand*)command;
+
+// Crashlytics
+- (void)setCrashlyticsCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)isCrashlyticsCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)logError:(CDVInvokedUrlCommand*)command;
+- (void)logMessage:(CDVInvokedUrlCommand*)command;
+- (void)sendCrash:(CDVInvokedUrlCommand*)command;
+- (void)setCrashlyticsUserId:(CDVInvokedUrlCommand*)command;
+
+// Remote config
+- (void)fetch:(CDVInvokedUrlCommand*)command;
+- (void)activateFetched:(CDVInvokedUrlCommand*)command;
+- (void)getValue:(CDVInvokedUrlCommand*)command;
+- (void)getInfo:(CDVInvokedUrlCommand*)command;
+
+// Performance
+- (void)setPerformanceCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)isPerformanceCollectionEnabled:(CDVInvokedUrlCommand*)command;
+- (void)startTrace:(CDVInvokedUrlCommand*)command;
+- (void)incrementCounter:(CDVInvokedUrlCommand*)command;
+- (void)stopTrace:(CDVInvokedUrlCommand*)command;
+
+// Firestore
+- (void)addDocumentToFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)setDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)updateDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)deleteDocumentFromFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)documentExistsInFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)fetchDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command;
+- (void)fetchFirestoreCollection:(CDVInvokedUrlCommand*)command;
+
+
+// Internals
++ (FirebasePlugin *) firebasePlugin;
++ (NSString*) appleSignInNonce;
++ (void) setFirestore:(FIRFirestore*) firestoreInstance;
+- (void) handlePluginExceptionWithContext: (NSException*) exception :(CDVInvokedUrlCommand*)command;
+- (void) handlePluginExceptionWithoutContext: (NSException*) exception;
+- (void) _logError: (NSString*)msg;
+- (void) _logInfo: (NSString*)msg;
+- (void) _logMessage: (NSString*)msg;
+- (BOOL) _shouldEnableCrashlytics;
+- (int) saveAuthCredential: (FIRAuthCredential *) authCredential;
+- (void)executeGlobalJavascript: (NSString*)jsString;
+
+- (void)createChannel:(CDVInvokedUrlCommand *)command;
+- (void)setDefaultChannel:(CDVInvokedUrlCommand *)command;
+- (void)deleteChannel:(CDVInvokedUrlCommand *)command;
+- (void)listChannels:(CDVInvokedUrlCommand *)command;
+
+@property (nonatomic, copy) NSString *notificationCallbackId;
+@property (nonatomic, copy) NSString *tokenRefreshCallbackId;
+@property (nonatomic, copy) NSString *apnsTokenRefreshCallbackId;
+@property (nonatomic, copy) NSString *googleSignInCallbackId;
+@property (nonatomic, copy) NSString *appleSignInCallbackId;
+
+@property (nonatomic, retain) NSMutableArray *notificationStack;
+@property (nonatomic, readwrite) NSMutableDictionary* traces;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.m b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.m
new file mode 100644
index 00000000..e72514c0
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePlugin.m
@@ -0,0 +1,1757 @@
+#import "FirebasePlugin.h"
+#import "FirebasePluginMessageReceiverManager.h"
+#import "AppDelegate+FirebasePlugin.h"
+#import <Cordova/CDV.h>
+#import "AppDelegate.h"
+#import <GoogleSignIn/GoogleSignIn.h>
+@import FirebaseInstanceID;
+@import FirebaseMessaging;
+@import FirebaseAnalytics;
+@import FirebaseRemoteConfig;
+@import FirebasePerformance;
+@import FirebaseAuth;
+@import UserNotifications;
+@import CommonCrypto;
+@import AuthenticationServices;
+
+@implementation FirebasePlugin
+
+@synthesize notificationCallbackId;
+@synthesize tokenRefreshCallbackId;
+@synthesize apnsTokenRefreshCallbackId;
+@synthesize googleSignInCallbackId;
+@synthesize appleSignInCallbackId;
+@synthesize notificationStack;
+@synthesize traces;
+
+static NSString*const LOG_TAG = @"FirebasePlugin[native]";
+static NSInteger const kNotificationStackSize = 10;
+static NSString*const FIREBASE_CRASHLYTICS_COLLECTION_ENABLED = @"FIREBASE_CRASHLYTICS_COLLECTION_ENABLED"; //preference
+static NSString*const FirebaseCrashlyticsCollectionEnabled = @"FirebaseCrashlyticsCollectionEnabled"; //plist
+static NSString*const FIREBASE_ANALYTICS_COLLECTION_ENABLED = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
+static NSString*const FIREBASE_PERFORMANCE_COLLECTION_ENABLED = @"FIREBASE_PERFORMANCE_COLLECTION_ENABLED";
+
+static FirebasePlugin* firebasePlugin;
+static BOOL registeredForRemoteNotifications = NO;
+static NSMutableDictionary* authCredentials;
+static NSString* currentNonce; // used for Apple Sign In
+static FIRFirestore* firestore;
+static NSUserDefaults* preferences;
+static NSDictionary* googlePlist;
+
+
++ (FirebasePlugin*) firebasePlugin {
+ return firebasePlugin;
+}
+
++ (NSString*) appleSignInNonce {
+ return currentNonce;
+}
+
++ (void) setFirestore:(FIRFirestore*) firestoreInstance{
+ firestore = firestoreInstance;
+}
+
+// @override abstract
+- (void)pluginInitialize {
+ NSLog(@"Starting Firebase plugin");
+ firebasePlugin = self;
+
+ @try {
+ preferences = [NSUserDefaults standardUserDefaults];
+ googlePlist = [NSMutableDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]];
+
+ if([self getGooglePlistFlagWithDefaultValue:FirebaseCrashlyticsCollectionEnabled defaultValue:YES]){
+ [self setPreferenceFlag:FIREBASE_CRASHLYTICS_COLLECTION_ENABLED flag:YES];
+ }
+
+ if([self getGooglePlistFlagWithDefaultValue:FIREBASE_ANALYTICS_COLLECTION_ENABLED defaultValue:YES]){
+ [self setPreferenceFlag:FIREBASE_ANALYTICS_COLLECTION_ENABLED flag:YES];
+ }
+
+ if([self getGooglePlistFlagWithDefaultValue:FIREBASE_PERFORMANCE_COLLECTION_ENABLED defaultValue:YES]){
+ [self setPreferenceFlag:FIREBASE_PERFORMANCE_COLLECTION_ENABLED flag:YES];
+ }
+
+ // Set actionable categories if pn-actions.json exist in bundle
+ [self setActionableNotifications];
+
+ // Check for permission and register for remote notifications if granted
+ [self _hasPermission:^(BOOL result) {}];
+
+ [GIDSignIn sharedInstance].presentingViewController = self.viewController;
+
+ authCredentials = [[NSMutableDictionary alloc] init];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+
+// Dynamic actions from pn-actions.json
+- (void)setActionableNotifications {
+
+ // Parse JSON
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"pn-actions" ofType:@"json"];
+ NSData *data = [NSData dataWithContentsOfFile:path];
+ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
+
+ // Assign actions for categories
+ NSMutableSet *categories = [[NSMutableSet alloc] init];
+ NSArray *actionsArray = [dict objectForKey:@"PushNotificationActions"];
+ for (NSDictionary *item in actionsArray) {
+ NSMutableArray *buttons = [NSMutableArray new];
+ NSString *category = [item objectForKey:@"category"];
+
+ NSArray *actions = [item objectForKey:@"actions"];
+ for (NSDictionary *action in actions) {
+ NSString *actionId = [action objectForKey:@"id"];
+ NSString *actionTitle = [action objectForKey:@"title"];
+
+ [buttons addObject:[UNNotificationAction actionWithIdentifier:actionId
+ title:NSLocalizedString(actionTitle, nil) options:UNNotificationActionOptionNone]];
+ }
+
+ [categories addObject:[UNNotificationCategory categoryWithIdentifier:category
+ actions:buttons intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]];
+ }
+
+ // Initialize categories
+ [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories];
+}
+
+// @override abstract
+- (void)handleOpenURL:(NSNotification*)notification{
+ NSURL* url = [notification object];
+ [[GIDSignIn sharedInstance] handleURL:url];
+}
+
+- (void)setAutoInitEnabled:(CDVInvokedUrlCommand *)command {
+ @try {
+ bool enabled = [[command.arguments objectAtIndex:0] boolValue];
+ [self runOnMainThread:^{
+ @try {
+ [FIRMessaging messaging].autoInitEnabled = enabled;
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)isAutoInitEnabled:(CDVInvokedUrlCommand *)command {
+ @try {
+
+ [self runOnMainThread:^{
+ @try {
+ bool enabled =[FIRMessaging messaging].isAutoInitEnabled;
+
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:enabled];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+/*
+ * Remote notifications
+ */
+
+- (void)getId:(CDVInvokedUrlCommand *)command {
+ __block CDVPluginResult *pluginResult;
+
+ FIRInstanceIDHandler handler = ^(NSString *_Nullable instID, NSError *_Nullable error) {
+ @try {
+ [self handleStringResultWithPotentialError:error command:command result:instID];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ };
+
+ @try {
+ [[FIRInstanceID instanceID] getIDWithHandler:handler];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)getToken:(CDVInvokedUrlCommand *)command {
+ @try {
+ [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
+ NSError * _Nullable error) {
+ NSString* token = nil;
+ if (error == nil && result != nil && result.token != nil) {
+ token = result.token;
+ }
+ [self handleStringResultWithPotentialError:error command:command result:token];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)getAPNSToken:(CDVInvokedUrlCommand *)command {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self getAPNSToken]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (NSString *)getAPNSToken {
+ NSString* hexToken = nil;
+ NSData* apnsToken = [FIRMessaging messaging].APNSToken;
+ if (apnsToken) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
+ // [deviceToken description] Starting with iOS 13 device token is like "{length = 32, bytes = 0xd3d997af 967d1f43 b405374a 13394d2f ... 28f10282 14af515f }"
+ hexToken = [self hexadecimalStringFromData:apnsToken];
+#else
+ hexToken = [[apnsToken.description componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet]invertedSet]]componentsJoinedByString:@""];
+#endif
+ }
+ return hexToken;
+}
+
+- (NSString *)hexadecimalStringFromData:(NSData *)data
+{
+ NSUInteger dataLength = data.length;
+ if (dataLength == 0) {
+ return nil;
+ }
+
+ const unsigned char *dataBuffer = data.bytes;
+ NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
+ for (int i = 0; i < dataLength; ++i) {
+ [hexString appendFormat:@"%02x", dataBuffer[i]];
+ }
+ return [hexString copy];
+}
+
+- (void)hasPermission:(CDVInvokedUrlCommand *)command {
+ @try {
+ [self _hasPermission:^(BOOL enabled) {
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:enabled];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+-(void)_hasPermission:(void (^)(BOOL result))completeBlock {
+ @try {
+ [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
+ @try {
+ BOOL enabled = NO;
+ if (settings.alertSetting == UNNotificationSettingEnabled) {
+ enabled = YES;
+ [self registerForRemoteNotifications];
+ }
+ NSLog(@"_hasPermission: %@", enabled ? @"YES" : @"NO");
+ completeBlock(enabled);
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+}
+
+- (void)grantPermission:(CDVInvokedUrlCommand *)command {
+ NSLog(@"grantPermission");
+ @try {
+ [self _hasPermission:^(BOOL enabled) {
+ @try {
+ if(enabled){
+ NSString* message = @"Permission is already granted - call hasPermission() to check before calling grantPermission()";
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }else{
+ [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate> _Nullable) self;
+ UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert|UNAuthorizationOptionSound|UNAuthorizationOptionBadge;
+ [[UNUserNotificationCenter currentNotificationCenter]
+ requestAuthorizationWithOptions:authOptions
+ completionHandler:^(BOOL granted, NSError * _Nullable error) {
+ @try {
+ NSLog(@"requestAuthorizationWithOptions: granted=%@", granted ? @"YES" : @"NO");
+ if (error == nil && granted) {
+ [self registerForRemoteNotifications];
+ }
+ [self handleBoolResultWithPotentialError:error command:command result:granted];
+
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }
+ ];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)registerForRemoteNotifications {
+ NSLog(@"registerForRemoteNotifications");
+ if(registeredForRemoteNotifications) return;
+
+ [self runOnMainThread:^{
+ @try {
+ [[UIApplication sharedApplication] registerForRemoteNotifications];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+ registeredForRemoteNotifications = YES;
+ }];
+}
+
+- (void)setBadgeNumber:(CDVInvokedUrlCommand *)command {
+ @try {
+ int number = [[command.arguments objectAtIndex:0] intValue];
+ [self runOnMainThread:^{
+ @try {
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:number];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)getBadgeNumber:(CDVInvokedUrlCommand *)command {
+ [self runOnMainThread:^{
+ @try {
+ long badge = [[UIApplication sharedApplication] applicationIconBadgeNumber];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:badge];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)subscribe:(CDVInvokedUrlCommand *)command {
+ @try {
+ NSString* topic = [NSString stringWithFormat:@"%@", [command.arguments objectAtIndex:0]];
+
+ [[FIRMessaging messaging] subscribeToTopic: topic completion:^(NSError * _Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)unsubscribe:(CDVInvokedUrlCommand *)command {
+ @try {
+ NSString* topic = [NSString stringWithFormat:@"%@", [command.arguments objectAtIndex:0]];
+
+ [[FIRMessaging messaging] unsubscribeFromTopic: topic completion:^(NSError * _Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)unregister:(CDVInvokedUrlCommand *)command {
+ @try {
+ [[FIRInstanceID instanceID] deleteIDWithHandler:^void(NSError *_Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+
+
+- (void)onMessageReceived:(CDVInvokedUrlCommand *)command {
+ @try {
+ self.notificationCallbackId = command.callbackId;
+
+ if (self.notificationStack != nil && [self.notificationStack count]) {
+ for (NSDictionary *userInfo in self.notificationStack) {
+ [self sendNotification:userInfo];
+ }
+ [self.notificationStack removeAllObjects];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)onTokenRefresh:(CDVInvokedUrlCommand *)command {
+ self.tokenRefreshCallbackId = command.callbackId;
+ @try {
+ [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
+ NSError * _Nullable error) {
+ @try {
+ if (result.token != nil && error == nil) {
+ [self sendToken:result.token];
+ }else{
+ [self handleStringResultWithPotentialError:error command:command result:result.token];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)onApnsTokenReceived:(CDVInvokedUrlCommand *)command {
+ self.apnsTokenRefreshCallbackId = command.callbackId;
+ @try {
+ NSString* apnsToken = [self getAPNSToken];
+ if(apnsToken != nil){
+ [self sendApnsToken:apnsToken];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)sendNotification:(NSDictionary *)userInfo {
+ @try {
+ if([FirebasePluginMessageReceiverManager sendNotification:userInfo]){
+ [self _logMessage:@"Message handled by custom receiver"];
+ return;
+ }
+ if (self.notificationCallbackId != nil) {
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:userInfo];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.notificationCallbackId];
+ } else {
+ if (!self.notificationStack) {
+ self.notificationStack = [[NSMutableArray alloc] init];
+ }
+
+ // stack notifications until a callback has been registered
+ [self.notificationStack addObject:userInfo];
+
+ if ([self.notificationStack count] >= kNotificationStackSize) {
+ [self.notificationStack removeLastObject];
+ }
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :self.commandDelegate];
+ }
+}
+
+- (void)sendToken:(NSString *)token {
+ @try {
+ if (self.tokenRefreshCallbackId != nil) {
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:token];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.tokenRefreshCallbackId];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :self.commandDelegate];
+ }
+}
+
+- (void)sendApnsToken:(NSString *)token {
+ @try {
+ if (self.apnsTokenRefreshCallbackId != nil) {
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:token];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.apnsTokenRefreshCallbackId];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :self.commandDelegate];
+ }
+}
+
+- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command {
+ [self runOnMainThread:^{
+ @try {
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+/*
+ * Authentication
+ */
+- (void)verifyPhoneNumber:(CDVInvokedUrlCommand *)command {
+ NSString* number = [command.arguments objectAtIndex:0];
+
+ @try {
+ [[FIRPhoneAuthProvider provider]
+ verifyPhoneNumber:number
+ UIDelegate:nil
+ completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
+
+ @try {
+ CDVPluginResult* pluginResult;
+ if (error) {
+ // Verification code not sent.
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description];
+ } else {
+ // Successful.
+ NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
+ [result setValue:@"false" forKey:@"instantVerification"];
+ [result setValue:verificationID forKey:@"verificationId"];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)createUserWithEmailAndPassword:(CDVInvokedUrlCommand*)command {
+ @try {
+ NSString* email = [command.arguments objectAtIndex:0];
+ NSString* password = [command.arguments objectAtIndex:1];
+ [[FIRAuth auth] createUserWithEmail:email
+ password:password
+ completion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ @try {
+ [self handleAuthResult:authResult error:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)signInUserWithEmailAndPassword:(CDVInvokedUrlCommand*)command {
+ @try {
+ NSString* email = [command.arguments objectAtIndex:0];
+ NSString* password = [command.arguments objectAtIndex:1];
+ [[FIRAuth auth] signInWithEmail:email
+ password:password
+ completion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ @try {
+ [self handleAuthResult:authResult error:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)signInUserWithCustomToken:(CDVInvokedUrlCommand*)command {
+ @try {
+ NSString* customToken = [command.arguments objectAtIndex:0];
+ [[FIRAuth auth] signInWithCustomToken:customToken
+ completion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ @try {
+ [self handleAuthResult:authResult error:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)signInUserAnonymously:(CDVInvokedUrlCommand*)command {
+ @try {
+ [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ @try {
+ [self handleAuthResult:authResult error:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)authenticateUserWithGoogle:(CDVInvokedUrlCommand*)command{
+ @try {
+ self.googleSignInCallbackId = command.callbackId;
+ [[GIDSignIn sharedInstance] signIn];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)authenticateUserWithApple:(CDVInvokedUrlCommand*)command{
+ @try {
+ CDVPluginResult *pluginResult;
+ if (@available(iOS 13.0, *)) {
+ self.appleSignInCallbackId = command.callbackId;
+ [self startSignInWithAppleFlow];
+
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ [pluginResult setKeepCallbackAsBool:YES];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"OS version is too low - Apple Sign In requires iOS 13.0+"];
+ }
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)signInWithCredential:(CDVInvokedUrlCommand*)command {
+ @try {
+ FIRAuthCredential* credential = [self obtainAuthCredential:[command.arguments objectAtIndex:0] command:command];
+ if(credential == nil) return;
+
+ [[FIRAuth auth] signInWithCredential:credential
+ completion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ [self handleAuthResult:authResult error:error command:command];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)reauthenticateWithCredential:(CDVInvokedUrlCommand*)command{
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ FIRAuthCredential* credential = [self obtainAuthCredential:[command.arguments objectAtIndex:0] command:command];
+ if(credential == nil) return;
+
+ [user reauthenticateWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
+ [self handleAuthResult:authResult error:error command:command];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)linkUserWithCredential:(CDVInvokedUrlCommand*)command {
+ @try {
+ FIRAuthCredential* credential = [self obtainAuthCredential:[command.arguments objectAtIndex:0] command:command];
+ if(credential == nil) return;
+
+ [[FIRAuth auth].currentUser linkWithCredential:credential
+ completion:^(FIRAuthDataResult * _Nullable authResult,
+ NSError * _Nullable error) {
+ [self handleAuthResult:authResult error:error command:command];
+ }];
+
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)isUserSignedIn:(CDVInvokedUrlCommand*)command {
+ @try {
+ bool isSignedIn = [FIRAuth auth].currentUser ? true : false;
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:isSignedIn] callbackId:command.callbackId];
+
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)signOutUser:(CDVInvokedUrlCommand*)command {
+ @try {
+ bool isSignedIn = [FIRAuth auth].currentUser ? true : false;
+ if(!isSignedIn){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ // Sign out of Google
+ if([[GIDSignIn sharedInstance] currentUser] != nil){
+ [[GIDSignIn sharedInstance] signOut];
+ }
+
+ // Sign out of Firebase
+ NSError *signOutError;
+ BOOL status = [[FIRAuth auth] signOut:&signOutError];
+ if (!status) {
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[NSString stringWithFormat:@"Error signing out: %@", signOutError]] callbackId:command.callbackId];
+ }else{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)getCurrentUser:(CDVInvokedUrlCommand *)command {
+
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+ [self extractAndReturnUserInfo:command];
+
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)reloadCurrentUser:(CDVInvokedUrlCommand *)command {
+
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+ [user reloadWithCompletion:^(NSError * _Nullable error) {
+ if (error != nil) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }else {
+ [self extractAndReturnUserInfo:command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void) extractAndReturnUserInfo:(CDVInvokedUrlCommand *)command {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ NSMutableDictionary* userInfo = [NSMutableDictionary new];
+ [userInfo setValue:user.displayName forKey:@"name"];
+ [userInfo setValue:user.email forKey:@"email"];
+ [userInfo setValue:@(user.isEmailVerified ? true : false) forKey:@"emailIsVerified"];
+ [userInfo setValue:user.phoneNumber forKey:@"phoneNumber"];
+ [userInfo setValue:user.photoURL ? user.photoURL.absoluteString : nil forKey:@"photoUrl"];
+ [userInfo setValue:user.uid forKey:@"uid"];
+ [userInfo setValue:@(user.isAnonymous ? true : false) forKey:@"isAnonymous"];
+ [user getIDTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
+ [userInfo setValue:token forKey:@"idToken"];
+ [user getIDTokenResultWithCompletion:^(FIRAuthTokenResult * _Nullable tokenResult, NSError * _Nullable error) {
+ [userInfo setValue:tokenResult.signInProvider forKey:@"providerId"];
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:userInfo] callbackId:command.callbackId];
+ }];
+ }];
+}
+
+- (void)updateUserProfile:(CDVInvokedUrlCommand*)command {
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ NSDictionary* profile = [command.arguments objectAtIndex:0];
+
+ FIRUserProfileChangeRequest* changeRequest = [user profileChangeRequest];
+ if([profile objectForKey:@"name"] != nil){
+ changeRequest.displayName = [profile objectForKey:@"name"];
+ }
+ if([profile objectForKey:@"photoUri"] != nil){
+ changeRequest.photoURL = [NSURL URLWithString:[profile objectForKey:@"photoUri"]];
+ }
+
+ [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)updateUserEmail:(CDVInvokedUrlCommand*)command {
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ NSString* email = [command.arguments objectAtIndex:0];
+ [user updateEmail:email completion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)sendUserEmailVerification:(CDVInvokedUrlCommand*)command{
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ [user sendEmailVerificationWithCompletion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)updateUserPassword:(CDVInvokedUrlCommand*)command{
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ NSString* password = [command.arguments objectAtIndex:0];
+ [user updatePassword:password completion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)sendUserPasswordResetEmail:(CDVInvokedUrlCommand*)command{
+ @try {
+ NSString* email = [command.arguments objectAtIndex:0];
+ [[FIRAuth auth] sendPasswordResetWithEmail:email completion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)deleteUser:(CDVInvokedUrlCommand*)command{
+ @try {
+ FIRUser* user = [FIRAuth auth].currentUser;
+ if(!user){
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
+ return;
+ }
+
+ [user deleteWithCompletion:^(NSError *_Nullable error) {
+ @try {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)startSignInWithAppleFlow API_AVAILABLE(ios(13.0)){
+ NSString *nonce = [self randomNonce:32];
+ currentNonce = nonce;
+ ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
+ ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest];
+ request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
+ request.nonce = [self stringBySha256HashingString:nonce];
+
+ ASAuthorizationController *authorizationController =
+ [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
+ authorizationController.delegate = [AppDelegate instance];
+ authorizationController.presentationContextProvider = [AppDelegate instance];
+ [authorizationController performRequests];
+}
+
+- (NSString *)stringBySha256HashingString:(NSString *)input {
+ const char *string = [input UTF8String];
+ unsigned char result[CC_SHA256_DIGEST_LENGTH];
+ CC_SHA256(string, (CC_LONG)strlen(string), result);
+
+ NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
+ for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
+ [hashed appendFormat:@"%02x", result[i]];
+ }
+ return hashed;
+}
+
+// Generates random nonce for Apple Sign In
+- (NSString *)randomNonce:(NSInteger)length {
+ NSAssert(length > 0, @"Expected nonce to have positive length");
+ NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
+ NSMutableString *result = [NSMutableString string];
+ NSInteger remainingLength = length;
+
+ while (remainingLength > 0) {
+ NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
+ for (NSInteger i = 0; i < 16; i++) {
+ uint8_t random = 0;
+ int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
+ NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
+
+ [randoms addObject:@(random)];
+ }
+
+ for (NSNumber *random in randoms) {
+ if (remainingLength == 0) {
+ break;
+ }
+
+ if (random.unsignedIntValue < characterSet.length) {
+ unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
+ [result appendFormat:@"%C", character];
+ remainingLength--;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Analytics
+ */
+- (void)setAnalyticsCollectionEnabled:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ BOOL enabled = [[command argumentAtIndex:0] boolValue];
+ CDVPluginResult* pluginResult;
+ [FIRAnalytics setAnalyticsCollectionEnabled:enabled];
+ [self setPreferenceFlag:FIREBASE_ANALYTICS_COLLECTION_ENABLED flag:enabled];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)isAnalyticsCollectionEnabled:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:[self getPreferenceFlag:FIREBASE_ANALYTICS_COLLECTION_ENABLED]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)logEvent:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* name = [command.arguments objectAtIndex:0];
+ NSDictionary *parameters = [command argumentAtIndex:1];
+
+ [FIRAnalytics logEventWithName:name parameters:parameters];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)setScreenName:(CDVInvokedUrlCommand *)command {
+ @try {
+ NSString* name = [command.arguments objectAtIndex:0];
+
+ [FIRAnalytics setScreenName:name screenClass:NULL];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (void)setUserId:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* id = [command.arguments objectAtIndex:0];
+
+ [FIRAnalytics setUserID:id];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)setUserProperty:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* name = [command.arguments objectAtIndex:0];
+ NSString* value = [command.arguments objectAtIndex:1];
+
+ [FIRAnalytics setUserPropertyString:value forName:name];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+/*
+ * Crashlytics
+ */
+
+- (void)setCrashlyticsCollectionEnabled:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ BOOL enabled = [[command argumentAtIndex:0] boolValue];
+ CDVPluginResult* pluginResult;
+ [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:enabled];
+ [self setPreferenceFlag:FIREBASE_CRASHLYTICS_COLLECTION_ENABLED flag:enabled];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)isCrashlyticsCollectionEnabled:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:[self isCrashlyticsEnabled]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+-(BOOL)isCrashlyticsEnabled{
+ return [self getPreferenceFlag:FIREBASE_CRASHLYTICS_COLLECTION_ENABLED];
+}
+
+- (void)logError:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ NSString* errorMessage = [command.arguments objectAtIndex:0];
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ @try {
+ if(![self isCrashlyticsEnabled]){
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot log error - Crashlytics collection is disabled"];
+ }
+ // We can optionally be passed a stack trace from stackTrace.js which we'll put in userInfo.
+ else if ([command.arguments count] > 1) {
+ NSArray* stackFrames = [command.arguments objectAtIndex:1];
+
+ NSString* message = errorMessage;
+ NSString* name = @"Uncaught Javascript exception";
+ NSMutableArray *customFrames = [[NSMutableArray alloc] init];
+ FIRExceptionModel *exceptionModel = [FIRExceptionModel exceptionModelWithName:name reason:message];
+
+ for (NSDictionary* stackFrame in stackFrames) {
+ FIRStackFrame *customFrame = [FIRStackFrame stackFrameWithSymbol:stackFrame[@"functionName"] file:stackFrame[@"fileName"] line:(uint32_t) [stackFrame[@"lineNumber"] intValue]];
+ [customFrames addObject:customFrame];
+ }
+ exceptionModel.stackTrace = customFrames;
+ [[FIRCrashlytics crashlytics] recordExceptionModel:exceptionModel];
+ }else{
+ //TODO detect and handle non-stack userInfo and pass to recordError
+ NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
+ NSError *error = [NSError errorWithDomain:errorMessage code:0 userInfo:userInfo];
+ [[FIRCrashlytics crashlytics] recordError:error];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } @catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+
+ }];
+}
+
+- (void)logMessage:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* message = [command argumentAtIndex:0 withDefault:@""];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ if(![self isCrashlyticsEnabled]){
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot log message - Crashlytics collection is disabled"];
+ }else if(message){
+ [[FIRCrashlytics crashlytics] logWithFormat:@"%@", message];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)sendCrash:(CDVInvokedUrlCommand*)command{
+ assert(NO);
+}
+
+- (void)setCrashlyticsUserId:(CDVInvokedUrlCommand *)command {
+ @try {
+ NSString* userId = [command.arguments objectAtIndex:0];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ if(![self isCrashlyticsEnabled]){
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot set user ID - Crashlytics collection is disabled"];
+ }else{
+ [[FIRCrashlytics crashlytics] setUserID:userId];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+/*
+ * Remote config
+ */
+- (void)fetch:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ FIRRemoteConfig* remoteConfig = [FIRRemoteConfig remoteConfig];
+
+ if ([command.arguments count] > 0) {
+ int expirationDuration = [[command.arguments objectAtIndex:0] intValue];
+
+ [remoteConfig fetchWithExpirationDuration:expirationDuration completionHandler:^(FIRRemoteConfigFetchStatus status, NSError * _Nullable error) {
+ if (status == FIRRemoteConfigFetchStatusSuccess && error == nil){
+ [self sendPluginSuccess:command];
+ }else if (error != nil) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ } else {
+ [self sendPluginError:command];
+ }
+ }];
+ } else {
+ [remoteConfig fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError * _Nullable error) {
+ if (status == FIRRemoteConfigFetchStatusSuccess && error == nil){
+ [self sendPluginSuccess:command];
+ }else if (error != nil) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ } else {
+ [self sendPluginError:command];
+ }
+ }];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)activateFetched:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ FIRRemoteConfig* remoteConfig = [FIRRemoteConfig remoteConfig];
+ [remoteConfig activateWithCompletion:^(BOOL changed, NSError* _Nullable error) {
+ [self handleBoolResultWithPotentialError:error command:command result:true];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)getValue:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* key = [command.arguments objectAtIndex:0];
+ FIRRemoteConfig* remoteConfig = [FIRRemoteConfig remoteConfig];
+ NSString* value = remoteConfig[key].stringValue;
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:value];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)getInfo:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ FIRRemoteConfig* remoteConfig = [FIRRemoteConfig remoteConfig];
+ NSInteger minimumFetchInterval = remoteConfig.configSettings.minimumFetchInterval;
+ NSInteger fetchTimeout = remoteConfig.configSettings.fetchTimeout;
+ NSDate* lastFetchTime = remoteConfig.lastFetchTime;
+ FIRRemoteConfigFetchStatus lastFetchStatus = remoteConfig.lastFetchStatus;
+ // isDeveloperModeEnabled is deprecated new recommnded way to check is minimumFetchInterval == 0
+ BOOL isDeveloperModeEnabled = minimumFetchInterval == 0 ? true : false;
+
+ NSDictionary* configSettings = @{
+ @"developerModeEnabled": [NSNumber numberWithBool:isDeveloperModeEnabled],
+ @"minimumFetchInterval": [NSNumber numberWithInteger:minimumFetchInterval],
+ @"fetchTimeout": [NSNumber numberWithInteger:fetchTimeout],
+ };
+
+ NSDictionary* infoObject = @{
+ @"configSettings": configSettings,
+ @"fetchTimeMillis": (lastFetchTime ? [NSNumber numberWithInteger:(lastFetchTime.timeIntervalSince1970 * 1000)] : [NSNull null]),
+ @"lastFetchStatus": [NSNumber numberWithInteger:(lastFetchStatus)],
+ };
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:infoObject];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+/*
+ * Performance
+ */
+- (void)setPerformanceCollectionEnabled:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ BOOL enabled = [[command argumentAtIndex:0] boolValue];
+ CDVPluginResult* pluginResult;
+ [[FIRPerformance sharedInstance] setDataCollectionEnabled:enabled];
+ [self setPreferenceFlag:FIREBASE_PERFORMANCE_COLLECTION_ENABLED flag:enabled];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)isPerformanceCollectionEnabled:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:[self getPreferenceFlag:FIREBASE_PERFORMANCE_COLLECTION_ENABLED]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)startTrace:(CDVInvokedUrlCommand *)command {
+
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* traceName = [command.arguments objectAtIndex:0];
+ FIRTrace *trace = [self.traces objectForKey:traceName];
+
+ if ( self.traces == nil) {
+ self.traces = [NSMutableDictionary new];
+ }
+
+ if (trace == nil) {
+ trace = [FIRPerformance startTraceWithName:traceName];
+ [self.traces setObject:trace forKey:traceName ];
+
+ }
+
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)incrementCounter:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* traceName = [command.arguments objectAtIndex:0];
+ NSString* counterNamed = [command.arguments objectAtIndex:1];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ FIRTrace *trace = (FIRTrace*)[self.traces objectForKey:traceName];
+
+ if (trace != nil) {
+ [trace incrementMetric:counterNamed byInt:1];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Trace not found"];
+ }
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)stopTrace:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* traceName = [command.arguments objectAtIndex:0];
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ FIRTrace *trace = [self.traces objectForKey:traceName];
+
+ if (trace != nil) {
+ [trace stop];
+ [self.traces removeObjectForKey:traceName];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Trace not found"];
+ }
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+/*
+* Firestore
+*/
+
+- (void)addDocumentToFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSDictionary* document = [command.arguments objectAtIndex:0];
+ NSString* collection = [command.arguments objectAtIndex:1];
+ __block FIRDocumentReference *ref =
+ [[firestore collectionWithPath:collection] addDocumentWithData:document completion:^(NSError * _Nullable error) {
+ [self handleStringResultWithPotentialError:error command:command result:ref.documentID];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)setDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* documentId = [command.arguments objectAtIndex:0];
+ NSDictionary* document = [command.arguments objectAtIndex:1];
+ NSString* collection = [command.arguments objectAtIndex:2];
+
+ [[[firestore collectionWithPath:collection] documentWithPath:documentId] setData:document completion:^(NSError * _Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)updateDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* documentId = [command.arguments objectAtIndex:0];
+ NSDictionary* document = [command.arguments objectAtIndex:1];
+ NSString* collection = [command.arguments objectAtIndex:2];
+
+ FIRDocumentReference* docRef = [[firestore collectionWithPath:collection] documentWithPath:documentId];
+ if(docRef != nil){
+ [docRef updateData:document completion:^(NSError * _Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+ }else{
+ [self sendPluginErrorWithMessage:@"Document not found in collection":command];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)deleteDocumentFromFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* documentId = [command.arguments objectAtIndex:0];
+ NSString* collection = [command.arguments objectAtIndex:1];
+
+ [[[firestore collectionWithPath:collection] documentWithPath:documentId]
+ deleteDocumentWithCompletion:^(NSError * _Nullable error) {
+ [self handleEmptyResultWithPotentialError:error command:command];
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)documentExistsInFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* documentId = [command.arguments objectAtIndex:0];
+ NSString* collection = [command.arguments objectAtIndex:1];
+
+ FIRDocumentReference* docRef = [[firestore collectionWithPath:collection] documentWithPath:documentId];
+ if(docRef != nil){
+ [docRef getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ BOOL docExists = snapshot.data != nil;
+ [self handleBoolResultWithPotentialError:error command:command result:docExists];
+ }];
+ }else{
+ [self sendPluginErrorWithMessage:@"Collection not found":command];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)fetchDocumentInFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* documentId = [command.arguments objectAtIndex:0];
+ NSString* collection = [command.arguments objectAtIndex:1];
+
+ FIRDocumentReference* docRef = [[firestore collectionWithPath:collection] documentWithPath:documentId];
+ if(docRef != nil){
+ [docRef getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ if (error != nil) {
+ [self sendPluginErrorWithMessage:error.localizedDescription:command];
+ } else if(snapshot.data != nil) {
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:snapshot.data] callbackId:command.callbackId];
+ }else{
+ [self sendPluginErrorWithMessage:@"Document not found in collection":command];
+ }
+ }];
+ }else{
+ [self sendPluginErrorWithMessage:@"Collection not found":command];
+ }
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+- (void)fetchFirestoreCollection:(CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* collection = [command.arguments objectAtIndex:0];
+ NSArray* filters = [command.arguments objectAtIndex:1];
+ FIRQuery* query = [firestore collectionWithPath:collection];
+
+ for (int i = 0; i < [filters count]; i++) {
+ NSArray* filter = [filters objectAtIndex:i];
+ if ([[filter objectAtIndex:0] isEqualToString:@"where"]) {
+ if ([[filter objectAtIndex:2] isEqualToString:@"=="]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] isEqualTo:[filter objectAtIndex:3]];
+ }
+ if ([[filter objectAtIndex:2] isEqualToString:@"<"]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] isLessThan:[filter objectAtIndex:3]];
+ }
+ if ([[filter objectAtIndex:2] isEqualToString:@">"]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThan:[filter objectAtIndex:3]];
+ }
+ if ([[filter objectAtIndex:2] isEqualToString:@"<="]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] isLessThanOrEqualTo:[filter objectAtIndex:3]];
+ }
+ if ([[filter objectAtIndex:2] isEqualToString:@">="]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThanOrEqualTo:[filter objectAtIndex:3]];
+ }
+ if ([[filter objectAtIndex:2] isEqualToString:@"array-contains"]) {
+ query = [query queryWhereField:[filter objectAtIndex:1] arrayContains:[filter objectAtIndex:3]];
+ }
+ continue;
+ }
+ if ([[filter objectAtIndex:0] isEqualToString:@"orderBy"]) {
+ query = [query queryOrderedByField:[filter objectAtIndex:1] descending:([[filter objectAtIndex:2] isEqualToString:@"desc"])];
+ continue;
+ }
+ if ([[filter objectAtIndex:0] isEqualToString:@"startAt"]) {
+ query = [query queryStartingAtValues:[filter objectAtIndex:1]];
+ continue;
+ }
+ if ([[filter objectAtIndex:0] isEqualToString:@"endAt"]) {
+ query = [query queryEndingAtValues:[filter objectAtIndex:1]];
+ continue;
+ }
+ if ([[filter objectAtIndex:0] isEqualToString:@"limit"]) {
+ query = [query queryLimitedTo:[(NSNumber *)[filter objectAtIndex:1] integerValue]];
+ continue;
+ }
+ }
+
+ [query getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ if (error != nil) {
+ [self sendPluginErrorWithMessage:error.localizedDescription:command];
+ } else {
+ NSMutableDictionary* documents = [[NSMutableDictionary alloc] init];;
+ for (FIRDocumentSnapshot *document in snapshot.documents) {
+ [documents setObject:document.data forKey:document.documentID];
+ }
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:documents] callbackId:command.callbackId];
+ }
+ }];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+ }];
+}
+
+/********************************/
+#pragma mark - utility functions
+/********************************/
+- (void) sendPluginSuccess:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
+}
+
+- (void) sendPluginError:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR] callbackId:command.callbackId];
+}
+
+- (void) sendPluginErrorWithMessage: (NSString*) errorMessage :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage];
+ [self _logError:errorMessage];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginErrorWithError:(NSError*)error command:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description] callbackId:command.callbackId];
+}
+
+- (void) handleEmptyResultWithPotentialError:(NSError*) error command:(CDVInvokedUrlCommand*)command {
+ if (error) {
+ [self sendPluginErrorWithError:error command:command];
+ }else{
+ [self sendPluginSuccess:command];
+ }
+}
+
+- (void) handleStringResultWithPotentialError:(NSError*) error command:(CDVInvokedUrlCommand*)command result:(NSString*)result {
+ if (error) {
+ [self sendPluginErrorWithError:error command:command];
+ }else{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:result] callbackId:command.callbackId];
+ }
+}
+
+- (void) handleBoolResultWithPotentialError:(NSError*) error command:(CDVInvokedUrlCommand*)command result:(BOOL)result {
+ if (error) {
+ [self sendPluginErrorWithError:error command:command];
+ }else{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:result] callbackId:command.callbackId];
+ }
+}
+
+- (void) handlePluginExceptionWithContext: (NSException*) exception :(CDVInvokedUrlCommand*)command
+{
+ [self handlePluginExceptionWithoutContext:exception];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:exception.reason];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) handlePluginExceptionWithoutContext: (NSException*) exception
+{
+ [self _logError:[NSString stringWithFormat:@"EXCEPTION: %@", exception.reason]];
+}
+
+- (void)executeGlobalJavascript: (NSString*)jsString
+{
+ [self.commandDelegate evalJs:jsString];
+}
+
+- (void)_logError: (NSString*)msg
+{
+ NSLog(@"%@ ERROR: %@", LOG_TAG, msg);
+ NSString* jsString = [NSString stringWithFormat:@"console.error(\"%@: %@\")", LOG_TAG, [self escapeJavascriptString:msg]];
+ [self executeGlobalJavascript:jsString];
+}
+
+- (void)_logInfo: (NSString*)msg
+{
+ NSLog(@"%@ INFO: %@", LOG_TAG, msg);
+ NSString* jsString = [NSString stringWithFormat:@"console.info(\"%@: %@\")", LOG_TAG, [self escapeJavascriptString:msg]];
+ [self executeGlobalJavascript:jsString];
+}
+
+- (void)_logMessage: (NSString*)msg
+{
+ NSLog(@"%@ LOG: %@", LOG_TAG, msg);
+ NSString* jsString = [NSString stringWithFormat:@"console.log(\"%@: %@\")", LOG_TAG, [self escapeJavascriptString:msg]];
+ [self executeGlobalJavascript:jsString];
+}
+
+- (NSString*)escapeJavascriptString: (NSString*)str
+{
+ NSString* result = [str stringByReplacingOccurrencesOfString: @"\\\"" withString: @"\""];
+ result = [result stringByReplacingOccurrencesOfString: @"\"" withString: @"\\\""];
+ result = [result stringByReplacingOccurrencesOfString: @"\n" withString: @"\\\n"];
+ return result;
+}
+
+- (void)runOnMainThread:(void (^)(void))completeBlock {
+ if (![NSThread isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ @try {
+ completeBlock();
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+ });
+ } else {
+ @try {
+ completeBlock();
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithoutContext:exception];
+ }
+ }
+}
+
+- (FIRAuthCredential*)obtainAuthCredential:(NSDictionary*)credential command:(CDVInvokedUrlCommand *)command {
+ FIRAuthCredential* authCredential = nil;
+
+ if(credential == nil){
+ NSString* errMsg = @"credential object must be passed as first and only argument";
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errMsg] callbackId:command.callbackId];
+ return authCredential;
+ }
+
+ NSString* key = [credential objectForKey:@"id"];
+ NSString* verificationId = [credential objectForKey:@"verificationId"];
+ NSString* code = [credential objectForKey:@"code"];
+
+ if(key != nil){
+ authCredential = [authCredentials objectForKey:key];
+ if(authCredential == nil){
+ NSString* errMsg = [NSString stringWithFormat:@"no native auth credential exists for specified id '%@'", key];
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errMsg] callbackId:command.callbackId];
+ }
+ }else if(verificationId != nil && code != nil){
+ authCredential = [[FIRPhoneAuthProvider provider]
+ credentialWithVerificationID:verificationId
+ verificationCode:code];
+ }else{
+ NSString* errMsg = @"credential object must either specify the id key of an existing native auth credential or the verificationId/code keys must be specified for a phone number authentication";
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errMsg] callbackId:command.callbackId];
+ }
+
+ return authCredential;
+}
+
+- (void) handleAuthResult:(FIRAuthDataResult*) authResult error:(NSError*) error command:(CDVInvokedUrlCommand*)command {
+ @try {
+ CDVPluginResult* pluginResult;
+ if (error) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description];
+ }else if (authResult == nil) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"User not signed in"];
+ }else{
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }@catch (NSException *exception) {
+ [self handlePluginExceptionWithContext:exception :command];
+ }
+}
+
+- (int) saveAuthCredential: (FIRAuthCredential*) authCredential {
+ int key = -1;
+ while (key < 0 || [authCredentials objectForKey:[NSNumber numberWithInt:key]] != nil) {
+ key = arc4random_uniform(100000);
+ }
+
+ [authCredentials setObject:authCredential forKey:[NSNumber numberWithInt:key]];
+
+ return key;
+}
+
+- (void) setPreferenceFlag:(NSString*) name flag:(BOOL)flag {
+ [preferences setBool:flag forKey:name];
+ [preferences synchronize];
+}
+
+- (BOOL) getPreferenceFlag:(NSString*) name {
+ if([preferences objectForKey:name] == nil){
+ return false;
+ }
+ return [preferences boolForKey:name];
+}
+
+- (BOOL) getGooglePlistFlagWithDefaultValue:(NSString*) name defaultValue:(BOOL)defaultValue {
+ if([googlePlist objectForKey:name] == nil){
+ return defaultValue;
+ }
+ return [[googlePlist objectForKey:name] isEqualToString:@"true"];
+}
+
+
+# pragma mark - Stubs
+- (void)createChannel:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+}
+
+- (void)setDefaultChannel:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+}
+
+- (void)deleteChannel:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+}
+
+- (void)listChannels:(CDVInvokedUrlCommand *)command {
+ [self.commandDelegate runInBackground:^{
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+}
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.h b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.h
new file mode 100644
index 00000000..639de9ba
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.h
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+
+@interface FirebasePluginMessageReceiver : NSObject {}
+- (bool) sendNotification:(NSDictionary *)userInfo;
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.m b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.m
new file mode 100644
index 00000000..0990d8c1
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiver.m
@@ -0,0 +1,17 @@
+#import "FirebasePluginMessageReceiver.h"
+#import "FirebasePluginMessageReceiverManager.h"
+
+@implementation FirebasePluginMessageReceiver
+
+- (id) init {
+ [FirebasePluginMessageReceiverManager register:self];
+ return self;
+}
+
+// Concrete subclasses should override this and return true if they handle the received message.
+- (bool) sendNotification:(NSDictionary *)userInfo {
+ NSAssert(false, @"You cannot call sendNotification on the FirebasePluginMessageReceiver class directly. Instead, you must override it using a subclass.");
+ return false;
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.h b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.h
new file mode 100644
index 00000000..96dac9d8
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.h
@@ -0,0 +1,6 @@
+#import "FirebasePluginMessageReceiver.h"
+
+@interface FirebasePluginMessageReceiverManager
++ (void) register:(FirebasePluginMessageReceiver *)receiver;
++ (bool) sendNotification:(NSDictionary *)userInfo;
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.m b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.m
new file mode 100644
index 00000000..7d463265
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/FirebasePluginMessageReceiverManager.m
@@ -0,0 +1,24 @@
+#import "FirebasePluginMessageReceiverManager.h"
+
+@implementation FirebasePluginMessageReceiverManager
+
+static NSMutableArray* receivers;
+
++ (void) register:(FirebasePluginMessageReceiver*)receiver {
+ if(receivers == nil){
+ receivers = [[NSMutableArray alloc] init];
+ }
+ [receivers addObject:receiver];
+}
+
++ (bool) sendNotification:(NSDictionary *)userInfo {
+ bool handled = false;
+ for(FirebasePluginMessageReceiver* receiver in receivers){
+ bool wasHandled = [receiver sendNotification:userInfo];
+ if(wasHandled){
+ handled = true;
+ }
+ }
+ return handled;
+}
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/GoogleService-Info.plist b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/GoogleService-Info.plist
new file mode 100644
index 00000000..5516ebf3
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-firebasex/src/ios/GoogleService-Info.plist
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+</dict>
+</plist> \ No newline at end of file