воскресенье, 20 сентября 2009 г.

Smooth integration Google Analytics in flex project

I propose a method for bloodless integration tracking in a finished project. Ideology is borrowing from the implementation of the automation logic in flex framework. For each class added to the stage (or concrete list of instances) defined the relevant tracking class.
The tracking classes can be dynamically loaded at runtime for reducing costs or implementation "by request" logic.


source

So, the main class responsible for tracking is GATrackingManager
package ua.org.enginer.managers
{
import com.google.analytics.AnalyticsTracker;
import com.google.analytics.GATracker;
import com.google.analytics.debug.VisualDebugMode;

import flash.display.DisplayObject;
import flash.events.Event;
import flash.utils.getQualifiedClassName;

import mx.core.Container;
import mx.core.UIComponent;
import mx.managers.SystemManager;

import ua.org.enginer.tracking.ButtonTrackingImpl;
import ua.org.enginer.tracking.ComboBoxTrackingImpl;
import ua.org.enginer.tracking.ContainerTrackingImpl;
import ua.org.enginer.tracking.ITrackingObject;
import ua.org.enginer.tracking.ListTrackingImpl;
import ua.org.enginer.tracking.UIComponentTrackingImpl;
import ua.org.enginer.tracking.ViewStackTrackingImpl;
import ua.org.enginer.utils.ClassUtils;

public class GATrackingManager {

private var account:String
private var tracker:AnalyticsTracker

function GATrackingManager(account:String) {
this.account = account
}

public function init(obj:Object):void {
// listen for popUps and etc.
SystemManager.getSWFRoot(obj).addEventListener(Event.ADDED, onAdded)

tracker = new GATracker(SystemManager.getSWFRoot(obj), account, "AS3", true);
tracker.debug.verbose = true;
tracker.debug.mode = VisualDebugMode.advanced;
tracker.debug.traceOutput = true;
//tracker.debug.GIFRequests = true;
}

public function accociateComponent(component:UIComponent):void {
if (!component) return;

var implementationClass:Class = findTrackingImplementation(component);
if (implementationClass) {
var implementation:ITrackingObject = new implementationClass(component);
implementation.tracker = tracker
implementationInstances.push(implementation);
} else {
trace("***no implementation:"+component+"=>"+getQualifiedClassName(component)+" <<"+implementationClass);
}
}

private function findTrackingImplementation(component:UIComponent):Class {
var componentName:String = getQualifiedClassName(component);

var componentClassName:String = componentName.split("::")[1]+"TrackingImpl";
var clazz:Class = findTrackingImplementationClassByName(componentClassName);
if (clazz) return clazz; // found tracking implementation

// That's it, let's not go higher in the chain.
if ("mx.core::UIComponent" == componentClassName) return null;


trace("\t search parent:"+component.className);
for each (componentName in ClassUtils.inheritance(component)) {
trace("\t\t :"+componentName);
componentClassName = componentName.split("::")[1]+"TrackingImpl";
clazz = findTrackingImplementationClassByName(componentClassName);
if (clazz) return clazz; // found tracking implementation
}
// not found
return null;
}

private function findTrackingImplementationClassByName(componentClassName:String):Class {
for each (var item:Class in implementationClasses) {
var implementationClassName:String = getQualifiedClassName(item);
if (implementationClassName.indexOf("::"+componentClassName) > 0) return item;
}
return null;
}

private function onAdded(event:Event):void {
if (event.target is UIComponent) {
accociateComponent(UIComponent(event.target))
if (event.target is Container) {
for each(var child:DisplayObject in Container(event.target).getChildren())
if (child is UIComponent) accociateComponent(UIComponent(child))
}

}
}

private var implementationInstances:Array = [];

private var implementationClasses:Array = [
UIComponentTrackingImpl,
ButtonTrackingImpl,
ContainerTrackingImpl,
ViewStackTrackingImpl,
ComboBoxTrackingImpl,
ListTrackingImpl
]
}
}
The private var implementationClasses:Array it is a storage for tracking implementation classes. ListTrackingImpl for example:
package ua.org.enginer.tracking
{
import flash.events.Event;

import mx.controls.List;

public class ListTrackingImpl extends UIComponentTrackingImpl
{
public function ListTrackingImpl(target:List) {
super(target)
List(target).addEventListener(Event.CHANGE, onChange)
}

protected function onChange(event:Event):void {
var item:Object = List(target).selectedItem
var value:String

if (item) {
if (item.hasOwnProperty('label')) {
value = item['label']
}else {
value = item.toString()
}
}

tracker.trackEvent(trackingName, "change", value)
}
}
}

extends UIComponentTrackingImpl

package ua.org.enginer.tracking
{
import com.google.analytics.AnalyticsTracker;

import flash.events.KeyboardEvent;
import flash.events.MouseEvent;

import mx.core.EventPriority;
import mx.core.UIComponent;

public class UIComponentTrackingImpl implements ITrackingObject
{
public var target:Object
private var _tracker:AnalyticsTracker

public function set tracker(value:AnalyticsTracker):void {
_tracker = value
}

public function get tracker():AnalyticsTracker {
return _tracker
}

public function UIComponentTrackingImpl(target:UIComponent)
{
this.target = target

target.addEventListener(MouseEvent.CLICK, mouseClickHandler, false, EventPriority.DEFAULT+1, true);
target.addEventListener(MouseEvent.DOUBLE_CLICK, mouseClickHandler, false, EventPriority.DEFAULT+1, true);
target.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, false, EventPriority.DEFAULT+1, true);
}

protected function mouseClickHandler(event:MouseEvent):void
{
if (trackingName && tracker) tracker.trackEvent(trackingName, "MouseEvent."+event.type)
}

protected function keyDownHandler(event:KeyboardEvent):void
{
if (trackingName && tracker) tracker.trackEvent(trackingName, "KeyboardEvent."+event.type)
}

public function get trackingName():String
{
if (target.hasOwnProperty('label'))
return target['label']

return target.automationName || target.toolTip || target.name || target.id
}
}
}
For starting tracking insert:
new GATrackingManager('UA-111-222').init(this)
there 'UA-111-222' - your Google Analytics account id.

Required library:
Another solutions: