diff --git a/README.md b/README.md index cd7d640b3..22e63e922 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,11 @@ Download your container-config json file from Tag Manager and add a resource-fil ... ``` +### iOS +Download your container-config json file from Tag Manager and add the file `GTM-5MFXXX.json` into xCode project root. Create a group name `container` and put in the file json. +![GTM xCode Project](gtm_xcode_project.png) + + ## Changing Notification Icon The plugin will use notification_icon from drawable resources if it exists, otherwise the default app icon will is used. @@ -257,6 +262,16 @@ Set a user property for use in Analytics: window.FirebasePlugin.setUserProperty("name", "value"); ``` +### setAnalyticsCollectionEnabled + +Enable/disable analytics collection + +``` +window.FirebasePlugin.setAnalyticsCollectionEnabled(true); // Enables analytics collection + +window.FirebasePlugin.setAnalyticsCollectionEnabled(false); // Disables analytics collection +``` + ### verifyPhoneNumber Request a verification ID and send a SMS with a verification code. Use them to construct a credential to sign in the user (in your app). @@ -267,22 +282,22 @@ Request a verification ID and send a SMS with a verification code. Use them to c **NOTE: This will only works on physical devices.** iOS will return: credential (string) -Android will return: +Android will return: credential.verificationId (object and with key verificationId) credential.instantVerification (boolean) -You need to use device plugin in order to access the right key. +You need to use device plugin in order to access the right key. IMPORTANT NOTE: Android supports auto-verify and instant device verification. Therefore in that cases it doesn't make sense to ask for sms code as you won't receive any. Also, **verificationId** will be *false* in this case. In order to sign the user in you need to check **credential.instantVerification**, if it's true, skip the SMS Code entry, call your backend server (sorry, the only way to succeed with this plugin) and pass over the phonenumber as param to identify the user (via ajax for example, using any endpoint to your backend). When using node.js Firebase Admin-SDK, follow this tutorial: - https://firebase.google.com/docs/auth/admin/create-custom-tokens -Pass back your custom generated token and call -```js +Pass back your custom generated token and call +```js firebase.auth().signInWithCustomToken(customTokenFromYourServer); ``` -instead of +instead of ``` firebase.auth().signInWithCredential(credential) ``` @@ -305,7 +320,7 @@ window.FirebasePlugin.verifyPhoneNumber(number, timeOutDuration, function(creden // sign in with the credential firebase.auth().signInWithCredential(credential); - + // call if credential.instantVerification was true (android only) firebase.auth().signInWithCustomToken(customTokenFromYourServer); @@ -455,12 +470,12 @@ Start a trace. window.FirebasePlugin.startTrace("test trace", success, error); ``` -### incrementCounter +### incrementMetric To count the performance-related events that occur in your app (such as cache hits or retries), add a line of code similar to the following whenever the event occurs, using a string other than retry to name that event if you are counting a different type of event: ``` -window.FirebasePlugin.incrementCounter("test trace", "retry", success, error); +window.FirebasePlugin.incrementMetric("test trace", "retry", success, error); ``` ### stopTrace @@ -471,12 +486,22 @@ Stop the trace window.FirebasePlugin.stopTrace("test trace"); ``` -### setAnalyticsCollectionEnabled +### startTraceHTTP -Enable/disable analytics collection +Start the HTTP trace. When it started, it return a traceId for identify the metric. ``` -window.FirebasePlugin.setAnalyticsCollectionEnabled(true); // Enables analytics collection +window.FirebasePlugin.stopTrace('https://www.google.com', 'GET', 0, function(traceId){ + console.log("HTTP Trace id: "+traceId); + }, error ); +``` -window.FirebasePlugin.setAnalyticsCollectionEnabled(false); // Disables analytics collection +### stopTraceHTTP + +Stop the HTTP trace + + + +``` +window.FirebasePlugin.stopTrace(TraceId, StatusCode, ContentType, payloadSize, success, error); ``` diff --git a/gtm_xcode_project.png b/gtm_xcode_project.png new file mode 100644 index 000000000..5133d15ed Binary files /dev/null and b/gtm_xcode_project.png differ diff --git a/package.json b/package.json index 7821acf14..17e8d1208 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/arnesson/cordova-plugin-firebase" }, "name": "cordova-plugin-firebase", - "version": "1.0.5", + "version": "1.0.6", "description": "Cordova plugin for Google Firebase", "cordova": { "id": "cordova-plugin-firebase", diff --git a/plugin.xml b/plugin.xml index 73d28fec6..3425e3b8d 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ - + Google Firebase Plugin MIT @@ -69,6 +69,8 @@ xmlns:android="http://schemas.android.com/apk/res/android"> + + @@ -94,23 +96,15 @@ xmlns:android="http://schemas.android.com/apk/res/android"> - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/src/android/FirebasePlugin.java b/src/android/FirebasePlugin.java index ded502912..8d258b495 100755 --- a/src/android/FirebasePlugin.java +++ b/src/android/FirebasePlugin.java @@ -6,6 +6,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.NotificationManagerCompat; +import android.telecom.Call; import android.util.Base64; import android.util.Log; @@ -16,6 +17,7 @@ import com.google.firebase.analytics.FirebaseAnalytics; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.perf.metrics.HttpMetric; import com.google.firebase.remoteconfig.FirebaseRemoteConfig; import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo; import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings; @@ -34,6 +36,7 @@ import org.json.JSONException; import org.json.JSONObject; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -87,6 +90,7 @@ public void run() { }); } + @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { if (action.equals("getInstanceId")) { @@ -177,15 +181,36 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo } else if (action.equals("verifyPhoneNumber")) { this.verifyPhoneNumber(callbackContext, args.getString(0), args.getInt(1)); return true; - } else if (action.equals("startTrace")) { + }else if (action.equals("enabeldPerformance")) { + this.enabeldPerformance(callbackContext, args.getBoolean(0) ); + }else if (action.equals("isPerformanceEnabled")) { + this.isPerformanceEnabled(callbackContext); + }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)); + } else if (action.equals("incrementMetric")) { + this.incrementMetric(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("startTraceHTTP")) { + String url = args.getString(0); + String method = args.getString(1); + long requestPayloadSize = 0; + if ( !args.isNull(2) ) + requestPayloadSize = args.getLong(2); + this.startTraceHTTP(callbackContext, url, method, requestPayloadSize); + return true; + }else if (action.equals("stopTraceHTTP")) { + String traceId = args.getString(0); + int statusCode = args.getInt(1); + String contentType = args.getString(2); + long responsePayloadSize = 0; + if ( !args.isNull(3) ) + responsePayloadSize = args.getLong(3); + this.stopTraceHTTP(callbackContext, traceId, statusCode, contentType, responsePayloadSize); + return true; } else if (action.equals("setAnalyticsCollectionEnabled")) { this.setAnalyticsCollectionEnabled(callbackContext, args.getBoolean(0)); return true; @@ -444,20 +469,49 @@ public void run() { }); } - private void logEvent(final CallbackContext callbackContext, final String name, final JSONObject params) - throws JSONException { + private void parseDataBundle(Bundle bundle, String key, Object value) throws JSONException{ + if ( value instanceof Float ){ + bundle.putFloat(key, ((Number) value).floatValue()); + } else if (value instanceof Integer ){ + bundle.putInt(key, ((Number) value).intValue() ); + } else if ( value instanceof Double) { + bundle.putDouble(key, ((Number) value).doubleValue() ); + } else if ( value instanceof Long ){ + bundle.putLong(key, ((Number) value).longValue() ); + } else if ( value instanceof JSONArray ){ + ArrayList aList = new ArrayList(); + JSONArray ja = (JSONArray)value; + + for (int i=0; i < ja.length(); i++){ + aList.add( this.makeBundle(ja.getJSONObject(i)) ); + } + + bundle.putParcelableArrayList(key, aList); + + }else if ( value instanceof JSONObject ){ + bundle.putBundle(key, this.makeBundle( (JSONObject)value ) ); + }else { + bundle.putString(key, value.toString()); + } + } + + private Bundle makeBundle(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()); - } + this.parseDataBundle(bundle, key, value); + } + return bundle; + } + + private void logEvent(final CallbackContext callbackContext, final String name, final JSONObject params) + throws JSONException { + + final Bundle bundle = this.makeBundle(params); cordova.getThreadPool().execute(new Runnable() { public void run() { @@ -475,10 +529,8 @@ private void logError(final CallbackContext callbackContext, final String messag cordova.getThreadPool().execute(new Runnable() { public void run() { try { - FirebaseCrash.report(new Exception(message)); callbackContext.success(1); } catch (Exception e) { - FirebaseCrash.log(e.getMessage()); e.printStackTrace(); callbackContext.error(e.getMessage()); } @@ -775,7 +827,19 @@ public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingTo // Firebase Performace // - private HashMap traces = new HashMap(); + private HashMap traces = new HashMap(); + private HashMap httpTraces = new HashMap(); + + + private void isPerformanceEnabled(final CallbackContext callbackContext){ + FirebasePerformance fp = FirebasePerformance.getInstance(); + callbackContext.success( fp.isPerformanceCollectionEnabled() ? "true" : "false" ); + } + + private void enabeldPerformance(final CallbackContext callbackContext, Boolean enabled){ + FirebasePerformance.getInstance().setPerformanceCollectionEnabled( enabled ); + callbackContext.success(); + } private void startTrace(final CallbackContext callbackContext, final String name) { final FirebasePlugin self = this; @@ -783,20 +847,25 @@ private void startTrace(final CallbackContext callbackContext, final String name public void run() { try { + FirebasePerformance fp = FirebasePerformance.getInstance(); + if (!fp.isPerformanceCollectionEnabled()){ + callbackContext.error("Firebase Performance is not enabled"); + } + + Trace myTrace = null; if (self.traces.containsKey(name)) { myTrace = self.traces.get(name); } if (myTrace == null) { - myTrace = FirebasePerformance.getInstance().newTrace(name); + myTrace = fp.newTrace(name); myTrace.start(); self.traces.put(name, myTrace); } callbackContext.success(); } catch (Exception e) { - FirebaseCrash.log(e.getMessage()); e.printStackTrace(); callbackContext.error(e.getMessage()); } @@ -804,7 +873,11 @@ public void run() { }); } - private void incrementCounter(final CallbackContext callbackContext, final String name, final String counterNamed) { + private void incrementMetric(final CallbackContext callbackContext, final String name, final String metricNamed){ + this.incrementMetric(callbackContext, name, metricNamed, 1); + } + + private void incrementMetric(final CallbackContext callbackContext, final String name, final String metricNamed, final long incrementBy) { final FirebasePlugin self = this; cordova.getThreadPool().execute(new Runnable() { public void run() { @@ -816,13 +889,12 @@ public void run() { } if (myTrace != null && myTrace instanceof Trace) { - myTrace.incrementCounter(counterNamed); + myTrace.incrementMetric(metricNamed, incrementBy); callbackContext.success(); } else { callbackContext.error("Trace not found"); } } catch (Exception e) { - FirebaseCrash.log(e.getMessage()); e.printStackTrace(); callbackContext.error(e.getMessage()); } @@ -849,7 +921,6 @@ public void run() { callbackContext.error("Trace not found"); } } catch (Exception e) { - FirebaseCrash.log(e.getMessage()); e.printStackTrace(); callbackContext.error(e.getMessage()); } @@ -857,6 +928,115 @@ public void run() { }); } + private void startTraceHTTP(final CallbackContext callbackContext, final String url, final String method, final long payloadSize){ + + final FirebasePlugin self = this; + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + + FirebasePerformance fp = FirebasePerformance.getInstance(); + if (!fp.isPerformanceCollectionEnabled()){ + callbackContext.error("Firebase Performance is not enabled"); + } + + + HttpMetric myTrace = null; + String aMethod = null; + + switch (method.toUpperCase()){ + case "GET": + aMethod = FirebasePerformance.HttpMethod.GET; + break; + case "PUT": + aMethod = FirebasePerformance.HttpMethod.PUT; + break; + case "POST": + aMethod = FirebasePerformance.HttpMethod.POST; + break; + case "DELETE": + aMethod = FirebasePerformance.HttpMethod.DELETE; + break; + case "HEAD": + aMethod = FirebasePerformance.HttpMethod.HEAD; + break; + case "PATCH": + aMethod = FirebasePerformance.HttpMethod.PATCH; + break; + case "OPTIONS": + aMethod = FirebasePerformance.HttpMethod.OPTIONS; + break; + case "TRACE": + aMethod = FirebasePerformance.HttpMethod.TRACE; + break; + case "CONNECT": + aMethod = FirebasePerformance.HttpMethod.CONNECT; + break; + } + + if ( aMethod == null){ + callbackContext.error("The HTTP method is not compatible"); + return; + } + + + myTrace = fp.newHttpMetric(url, aMethod); + + if ( payloadSize > 0 ) + myTrace.setRequestPayloadSize(payloadSize); + + myTrace.start(); + + String traceName = myTrace.toString(); + self.httpTraces.put(traceName, myTrace); + + callbackContext.success(traceName); + + + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage()); + } + } + }); + + } + + private void stopTraceHTTP(final CallbackContext callbackContext, final String traceId, final int responseCode, final String contentType, final long payLoadSize){ + final FirebasePlugin self = this; + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + + HttpMetric myTrace = null; + if (self.httpTraces.containsKey(traceId)) { + myTrace = self.httpTraces.get(traceId); + } + + if (myTrace != null && myTrace instanceof HttpMetric) { // + + myTrace.setResponseContentType(contentType); + myTrace.setResponsePayloadSize(payLoadSize); + myTrace.setHttpResponseCode(responseCode); + myTrace.stop(); + + self.httpTraces.remove(traceId); + callbackContext.success(); + + } else { + callbackContext.error("Trace not found"); + } + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage()); + } + } + }); + } + + + // End performace + private void setAnalyticsCollectionEnabled(final CallbackContext callbackContext, final boolean enabled) { final FirebasePlugin self = this; cordova.getThreadPool().execute(new Runnable() { diff --git a/src/ios/FirebasePlugin.h b/src/ios/FirebasePlugin.h index 5de767cab..87731c3fd 100755 --- a/src/ios/FirebasePlugin.h +++ b/src/ios/FirebasePlugin.h @@ -27,13 +27,18 @@ - (void)fetch:(CDVInvokedUrlCommand*)command; - (void)activateFetched:(CDVInvokedUrlCommand*)command; - (void)getValue:(CDVInvokedUrlCommand*)command; +- (void)enabeldPerformance:(CDVInvokedUrlCommand*)command; +- (void)isPerformanceEnabled:(CDVInvokedUrlCommand*)command; - (void)startTrace:(CDVInvokedUrlCommand*)command; - (void)incrementCounter:(CDVInvokedUrlCommand*)command; - (void)stopTrace:(CDVInvokedUrlCommand*)command; +- (void)startTraceHTTP:(CDVInvokedUrlCommand*)command; +- (void)stopTraceHTTP:(CDVInvokedUrlCommand*)command; - (void)setAnalyticsCollectionEnabled:(CDVInvokedUrlCommand*)command; @property (nonatomic, copy) NSString *notificationCallbackId; @property (nonatomic, copy) NSString *tokenRefreshCallbackId; @property (nonatomic, retain) NSMutableArray *notificationStack; @property (nonatomic, readwrite) NSMutableDictionary* traces; +@property (nonatomic, readwrite) NSMutableDictionary* httpTraces; @end diff --git a/src/ios/FirebasePlugin.m b/src/ios/FirebasePlugin.m index 311c0ded0..92dd0ee68 100644 --- a/src/ios/FirebasePlugin.m +++ b/src/ios/FirebasePlugin.m @@ -378,6 +378,27 @@ - (void)getValue:(CDVInvokedUrlCommand *)command { // // Performace // + +- (void)isPerformanceEnabled:(CDVInvokedUrlCommand *)command { + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool: [FIRPerformance sharedInstance].isInstrumentationEnabled && [FIRPerformance sharedInstance].isDataCollectionEnabled ]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)enabeldPerformance:(CDVInvokedUrlCommand *)command { + [self.commandDelegate runInBackground:^{ + NSNumber *enabled = [command.arguments objectAtIndex:0]; + if([enabled isEqual: @(YES)]){ + [FIRPerformance sharedInstance].instrumentationEnabled = YES; + [FIRPerformance sharedInstance].dataCollectionEnabled = YES; + }else{ + [FIRPerformance sharedInstance].instrumentationEnabled = NO; + [FIRPerformance sharedInstance].dataCollectionEnabled = NO; + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + - (void)startTrace:(CDVInvokedUrlCommand *)command { [self.commandDelegate runInBackground:^{ @@ -437,6 +458,87 @@ - (void)stopTrace:(CDVInvokedUrlCommand *)command { }]; } +-(void)startTraceHTTP:(CDVInvokedUrlCommand *)command{ + [self.commandDelegate runInBackground:^{ + + if ( self.httpTraces == nil) { + self.httpTraces = [NSMutableDictionary new]; + } + + NSString *url = [command.arguments objectAtIndex:0]; + NSString *method = [command.arguments objectAtIndex:1]; + long requestPayloadSize = [[command.arguments objectAtIndex:2] longValue]; + + method = [method uppercaseString]; + FIRHTTPMethod aMethod = false; + + if ( [method isEqualToString:@"GET"] ){ + aMethod = FIRHTTPMethodGET; + }else if ( [method isEqualToString:@"PUT"] ){ + aMethod = FIRHTTPMethodPUT; + }else if ( [method isEqualToString:@"POST"] ){ + aMethod = FIRHTTPMethodPOST; + }else if ( [method isEqualToString:@"DELETE"] ){ + aMethod = FIRHTTPMethodDELETE; + }else if ( [method isEqualToString:@"HEAD"] ){ + aMethod = FIRHTTPMethodHEAD; + }else if ( [method isEqualToString:@"PATCH"] ){ + aMethod = FIRHTTPMethodPATCH; + }else if ( [method isEqualToString:@"OPTIONS"] ){ + aMethod = FIRHTTPMethodOPTIONS; + }else if ( [method isEqualToString:@"TRACE"] ){ + aMethod = FIRHTTPMethodTRACE; + }else if ( [method isEqualToString:@"CONNECT"] ){ + aMethod = FIRHTTPMethodCONNECT; + } + + @try { + FIRHTTPMetric *metric = [[FIRHTTPMetric alloc] initWithURL:[NSURL URLWithString:url] HTTPMethod:aMethod]; + if ( requestPayloadSize > 0 ) + [metric setRequestPayloadSize:requestPayloadSize]; + [metric start]; + + NSString *traceName = [metric description]; + + [self.httpTraces setObject:metric forKey:traceName ]; + + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:traceName]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + @catch (NSException *exception) { + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The HTTP method is not compatible"] callbackId:command.callbackId]; + } + + + }]; +} + +-(void)stopTraceHTTP:(CDVInvokedUrlCommand *)command{ + [self.commandDelegate runInBackground:^{ + + NSString *traceId = [command.arguments objectAtIndex:0]; + NSInteger statusCode = [[command.arguments objectAtIndex:1] integerValue]; + NSString *contentType = [command.arguments objectAtIndex:2]; + long responsePayloadSize = [[command.arguments objectAtIndex:3] longValue]; + + FIRHTTPMetric *metric = [self.httpTraces objectForKey:traceId]; + if ( metric != nil ){ + [metric setResponseCode:statusCode]; + [metric setResponseContentType:contentType]; + if ( responsePayloadSize > 0 ) + [metric setResponsePayloadSize:responsePayloadSize]; + [metric stop]; + [self.httpTraces removeObjectForKey:traceId]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }else{ + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Trace not found"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + + }]; +} + - (void)setAnalyticsCollectionEnabled:(CDVInvokedUrlCommand *)command { [self.commandDelegate runInBackground:^{ BOOL enabled = [[command argumentAtIndex:0] boolValue]; diff --git a/www/firebase.js b/www/firebase.js index c156b02b1..012630750 100644 --- a/www/firebase.js +++ b/www/firebase.js @@ -128,6 +128,20 @@ exports.setDefaults = function (defaults, namespace, success, error) { exec(success, error, "FirebasePlugin", "setDefaults", args); }; +exports.enabeldPerformance = function (enabled, success, error) { + exec(success, error, "FirebasePlugin", "enabeldPerformance", [enabled]); +}; + +exports.isPerformanceEnabled = function (success, error) { + var oSuccess = success; + success = function(enabled){ + if (typeof oSuccess == 'function'){ + oSuccess(enabled === "true"); + } + } + exec(success, error, "FirebasePlugin", "isPerformanceEnabled", []); +}; + exports.startTrace = function (name, success, error) { exec(success, error, "FirebasePlugin", "startTrace", [name]); }; @@ -140,6 +154,14 @@ exports.stopTrace = function (name, success, error) { exec(success, error, "FirebasePlugin", "stopTrace", [name]); }; +exports.startTraceHTTP = function (url, method, payloadSize, success, error ){ + exec(success, error, "FirebasePlugin", "startTraceHTTP", [url, method, payloadSize]); +} + +exports.stopTraceHTTP = function (traceId, statusCode, contentType, payloadSize, success, error ){ + exec(success, error, "FirebasePlugin", "stopTraceHTTP", [traceId, statusCode, contentType, payloadSize]); +} + exports.setAnalyticsCollectionEnabled = function (enabled, success, error) { exec(success, error, "FirebasePlugin", "setAnalyticsCollectionEnabled", [enabled]); };