diff --git a/build.xml b/build.xml
index 8f1c25e..2406af8 100755
--- a/build.xml
+++ b/build.xml
@@ -41,8 +41,7 @@
-
-
+
@@ -71,14 +70,11 @@
-
+
-
-
-
-
-
+
+
diff --git a/source/FeathersSDKManagerContext.as b/source/FeathersSDKManagerContext.as
index 6b3cb61..5028d2e 100644
--- a/source/FeathersSDKManagerContext.as
+++ b/source/FeathersSDKManagerContext.as
@@ -16,11 +16,10 @@ limitations under the License.
*/
package
{
- import com.gamua.flox.Flox;
+ import commands.AnalyticsInitCommand;
- import commands.FloxInitCommand;
- import commands.FloxLogErrorCommand;
- import commands.FloxLogEventInstallCompleteCommand;
+ import commands.AnalyticsErrorCommand;
+ import commands.AnalyticsEventInstallCompleteCommand;
import events.AcquireProductServiceEventType;
import events.RunInstallScriptServiceEventType;
@@ -79,12 +78,13 @@ package
var applicationVersion:String = applicationDescriptor.ns::versionNumber.toString();
this.injector.mapValue(String, applicationVersion, "applicationVersion");
- CONFIG::USE_FLOX
+ CONFIG::USE_ANALYTICS
{
- this.commandMap.mapEvent(ContextEventType.STARTUP_COMPLETE, FloxInitCommand);
- this.commandMap.mapEvent(RunInstallScriptServiceEventType.COMPLETE, FloxLogEventInstallCompleteCommand);
- this.commandMap.mapEvent(RunInstallScriptServiceEventType.ERROR, FloxLogErrorCommand);
- this.commandMap.mapEvent(AcquireProductServiceEventType.ERROR, FloxLogErrorCommand);
+ this.commandMap.mapEvent(ContextEventType.STARTUP_COMPLETE, AnalyticsInitCommand);
+ this.commandMap.mapEvent(RunInstallScriptServiceEventType.COMPLETE, AnalyticsEventInstallCompleteCommand);
+ this.commandMap.mapEvent(RunInstallScriptServiceEventType.ERROR, AnalyticsErrorCommand);
+ this.commandMap.mapEvent(AcquireProductServiceEventType.ERROR, AnalyticsErrorCommand);
+ this.commandMap.mapEvent(UncaughtErrorEvent.UNCAUGHT_ERROR, AnalyticsErrorCommand);
Starling.current.nativeStage.root.loaderInfo.uncaughtErrorEvents.addEventListener(
UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorEvents_uncaughtErrorEventHandler);
}
@@ -108,11 +108,11 @@ package
if(error is Error)
{
var errorError:Error = Error(error);
- Flox.logError(error, "Uncaught Error: " + errorError.message);
+ this.dispatchEventWith(UncaughtErrorEvent.UNCAUGHT_ERROR, false, errorError.message);
}
else
{
- Flox.logError("UncaughtError", error);
+ this.dispatchEventWith(UncaughtErrorEvent.UNCAUGHT_ERROR, false, error);
}
}
}
diff --git a/source/com/bowlerhatsoftware/analytics/GAMeasurementProtocol.as b/source/com/bowlerhatsoftware/analytics/GAMeasurementProtocol.as
new file mode 100644
index 0000000..d8e6f38
--- /dev/null
+++ b/source/com/bowlerhatsoftware/analytics/GAMeasurementProtocol.as
@@ -0,0 +1,223 @@
+/*
+Copyright 2015 Bowler Hat LLC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package com.bowlerhatsoftware.analytics
+{
+ import flash.events.Event;
+ import flash.events.IOErrorEvent;
+ import flash.events.SecurityErrorEvent;
+ import flash.net.URLLoader;
+ import flash.net.URLRequest;
+ import flash.net.URLRequestMethod;
+ import flash.net.URLVariables;
+
+ /**
+ * A simple implementation of the Google Analytics Measurement Protocol.
+ */
+ public class GAMeasurementProtocol
+ {
+ /**
+ * @private
+ */
+ internal static var loaders:Vector. = new [];
+
+ /**
+ * @private
+ */
+ private static var PRODUCTION_URL:String = "https://www.google-analytics.com/collect";
+
+ /**
+ * @private
+ */
+ private static var DEBUG_URL:String = "https://www.google-analytics.com/debug/collect";
+
+ /**
+ * The Google Analytics tracking ID for your property. Has the following
+ * format: UA-XXXX-Y. Required.
+ */
+ public static var trackingID:String;
+
+ /**
+ * An anonymous client ID. Required.
+ */
+ public static var clientID:String;
+
+ /**
+ * The name of your application.
+ */
+ public static var applicationName:String;
+
+ /**
+ * The version of your application.
+ */
+ public static var applicationVersion:String;
+
+ /**
+ * When true
, the data will be sent to the Google Analytics
+ * Measurement Protocol Validation Server. The result will be displayed
+ * in the console.
+ */
+ public static var debugMode:Boolean = false;
+
+ /**
+ * Tracks an event.
+ */
+ public static function trackEvent(eventCategory:String, eventAction:String, eventLabel:String = null, eventValue:int = -1):void
+ {
+ var parameters:URLVariables = createURLVariablesWithDefaults();
+ parameters.t = "event";
+ parameters.ec = eventCategory;
+ parameters.ea = eventAction;
+ if(eventLabel !== null && eventLabel.length > 0)
+ {
+ parameters.el = eventLabel;
+ }
+ if(eventValue >= 0)
+ {
+ parameters.ev = eventValue.toString();
+ }
+
+ loadURLRequestWithParameters(parameters);
+ }
+
+ /**
+ * Tracks an exception.
+ */
+ public static function trackException(exceptionDescription:String, isFatal:Boolean):void
+ {
+ var parameters:URLVariables = createURLVariablesWithDefaults();
+ parameters.t = "exception";
+ parameters.exd = exceptionDescription;
+ parameters.exf = isFatal ? "1" : "0";
+
+ loadURLRequestWithParameters(parameters);
+ }
+
+ /**
+ * @private
+ */
+ private static function validateGlobalParameters():void
+ {
+ if(GAMeasurementProtocol.trackingID === null || GAMeasurementProtocol.trackingID.length === 0)
+ {
+ throw new ArgumentError("MeasurementProtocol.trackingID cannot be null.")
+ }
+ if(GAMeasurementProtocol.clientID === null || GAMeasurementProtocol.clientID.length === 0)
+ {
+ throw new ArgumentError("MeasurementProtocol.clientID cannot be null.")
+ }
+ }
+
+ /**
+ * @private
+ */
+ private static function createURLVariablesWithDefaults():URLVariables
+ {
+ validateGlobalParameters();
+ var parameters:URLVariables = new URLVariables();
+ parameters.v = "1";
+ parameters.tid = trackingID;
+ parameters.cid = clientID;
+ if(applicationName !== null && applicationName.length > 0)
+ {
+ parameters.an = applicationName;
+ }
+ if(applicationVersion !== null && applicationVersion.length > 0)
+ {
+ parameters.av = applicationVersion;
+ }
+ return parameters;
+ }
+
+ /**
+ * @private
+ */
+ private static function loadURLRequestWithParameters(parameters:URLVariables):void
+ {
+ var url:String = debugMode ? DEBUG_URL : PRODUCTION_URL;
+ var request:URLRequest = new URLRequest(url);
+ request.method = URLRequestMethod.POST;
+ request.data = parameters;
+
+ var loader:URLLoader = new URLLoader();
+ loader.addEventListener(Event.COMPLETE, loader_completeHandler);
+ loader.addEventListener(IOErrorEvent.IO_ERROR, loader_ioErrorHandler);
+ loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loader_securityErrorHandler);
+ loaders[loaders.length] = loader;
+ loader.load(request);
+ }
+
+ /**
+ * @private
+ */
+ private static function cleanupLoader(loader:URLLoader):void
+ {
+ loader.removeEventListener(Event.COMPLETE, loader_completeHandler);
+ loader.removeEventListener(IOErrorEvent.IO_ERROR, loader_ioErrorHandler);
+ var index:int = loaders.indexOf(loader);
+ if(index === 0)
+ {
+ loaders.shift();
+ }
+ else if(index === loaders.length - 1)
+ {
+ loaders.pop();
+ }
+ else
+ {
+ loaders.splice(index, 1);
+ }
+ }
+
+ /**
+ * @private
+ */
+ private static function loader_completeHandler(event:Event):void
+ {
+ var loader:URLLoader = URLLoader(event.currentTarget);
+ cleanupLoader(loader);
+ if(debugMode)
+ {
+ trace(loader.data);
+ }
+ }
+
+ /**
+ * @private
+ */
+ private static function loader_ioErrorHandler(event:IOErrorEvent):void
+ {
+ var loader:URLLoader = URLLoader(event.currentTarget);
+ cleanupLoader(loader);
+ if(debugMode)
+ {
+ trace(event);
+ }
+ }
+
+ /**
+ * @private
+ */
+ private static function loader_securityErrorHandler(event:SecurityErrorEvent):void
+ {
+ var loader:URLLoader = URLLoader(event.currentTarget);
+ cleanupLoader(loader);
+ if(debugMode)
+ {
+ trace(event);
+ }
+ }
+ }
+}
diff --git a/source/commands/FloxLogErrorCommand.as b/source/commands/AnalyticsErrorCommand.as
similarity index 75%
rename from source/commands/FloxLogErrorCommand.as
rename to source/commands/AnalyticsErrorCommand.as
index 0ac7846..bccd385 100644
--- a/source/commands/FloxLogErrorCommand.as
+++ b/source/commands/AnalyticsErrorCommand.as
@@ -16,15 +16,14 @@ limitations under the License.
*/
package commands
{
- import com.gamua.flox.Flox;
-
import model.SDKManagerModel;
+ import com.bowlerhatsoftware.analytics.GAMeasurementProtocol;
import org.robotlegs.starling.mvcs.Command;
import starling.events.Event;
- public class FloxLogErrorCommand extends Command
+ public class AnalyticsErrorCommand extends Command
{
[Inject]
public var event:Event;
@@ -34,8 +33,9 @@ package commands
override public function execute():void
{
- var message:String = event.data as String;
- Flox.logError(this.event.type, message)
+ var errorType:String = this.event.type;
+ var message:String = this.event.data as String;
+ GAMeasurementProtocol.trackException(errorType + ": " + message, true);
}
}
}
diff --git a/source/commands/FloxLogEventInstallCompleteCommand.as b/source/commands/AnalyticsEventInstallCompleteCommand.as
similarity index 69%
rename from source/commands/FloxLogEventInstallCompleteCommand.as
rename to source/commands/AnalyticsEventInstallCompleteCommand.as
index c03f8fa..49f31f7 100644
--- a/source/commands/FloxLogEventInstallCompleteCommand.as
+++ b/source/commands/AnalyticsEventInstallCompleteCommand.as
@@ -16,31 +16,22 @@ limitations under the License.
*/
package commands
{
- import com.gamua.flox.Flox;
-
import model.SDKManagerModel;
+ import com.bowlerhatsoftware.analytics.GAMeasurementProtocol;
+
import org.robotlegs.starling.mvcs.Command;
- public class FloxLogEventInstallCompleteCommand extends Command
+ public class AnalyticsEventInstallCompleteCommand extends Command
{
- private static const EVENT_NAME:String = "InstallComplete";
-
[Inject]
public var sdkManagerModel:SDKManagerModel;
override public function execute():void
{
var productVersion:String = sdkManagerModel.selectedProduct.versionNumber;
- var runtimeVersion:String = sdkManagerModel.selectedRuntime.airVersionNumber;
var operatingSystem:String = sdkManagerModel.operatingSystem;
- Flox.logEvent(EVENT_NAME,
- {
- productVersion: productVersion,
- runtimeVersion: runtimeVersion,
- operatingSystem: operatingSystem
- });
- Flox.shutdown();
+ GAMeasurementProtocol.trackEvent("InstallComplete", operatingSystem, productVersion);
}
}
}
diff --git a/source/commands/AnalyticsInitCommand.as b/source/commands/AnalyticsInitCommand.as
new file mode 100644
index 0000000..3d02ef1
--- /dev/null
+++ b/source/commands/AnalyticsInitCommand.as
@@ -0,0 +1,36 @@
+package commands
+{
+ import flash.net.SharedObject;
+
+ import mx.utils.UIDUtil;
+
+ import com.bowlerhatsoftware.analytics.GAMeasurementProtocol;
+ import org.robotlegs.starling.mvcs.Command;
+
+ public class AnalyticsInitCommand extends Command
+ {
+ [Inject(name="applicationVersion")]
+ public var applicationVersion:String;
+
+ override public function execute():void
+ {
+ GAMeasurementProtocol.debugMode = false;
+ GAMeasurementProtocol.trackingID = CONFIG::ANALYTICS_TRACKING_ID;
+ GAMeasurementProtocol.clientID = getUID();
+ GAMeasurementProtocol.applicationName = "Feathers SDK Manager";
+ GAMeasurementProtocol.applicationVersion = applicationVersion;
+ }
+
+ private function getUID():String
+ {
+ var so:SharedObject = SharedObject.getLocal("uid");
+ var uid:String = so.data.uid;
+ if(uid === null)
+ {
+ so.data.uid = uid = UIDUtil.createUID();
+ so.flush();
+ }
+ return uid;
+ }
+ }
+}
diff --git a/source/commands/FloxInitCommand.as b/source/commands/FloxInitCommand.as
deleted file mode 100644
index 9a6fe00..0000000
--- a/source/commands/FloxInitCommand.as
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Feathers SDK Manager
-Copyright 2015 Bowler Hat LLC
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-package commands
-{
- import com.gamua.flox.Flox;
-
- import org.robotlegs.starling.mvcs.Command;
-
- public class FloxInitCommand extends Command
- {
- [Inject(name="applicationVersion")]
- public var applicationVersion:String;
-
- override public function execute():void
- {
- Flox.init(CONFIG::FLOX_ID, CONFIG::FLOX_KEY, this.applicationVersion);
- }
- }
-}