diff options
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-firebasex/src/android')
9 files changed, 2995 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 |
