AIRKinect Extension v1 Live
Check out the AIRKinect Extension version 1. It has taken a lot of work for everyone to learn C++ and what it takes to make a native extension, but its out and ready to be pushed to the limits. Thanks for all the hard work as3NUI guys, especially Ross Gerbasi!
Check it out - http://www.as3nui.com/air-kinect/
AIR Extensions
My life for the next Month:
<extension xmlns="http://ns.adobe.com/air/extension/2.5"> <name>Microsoft Kinect Extension for ActionScript</name> <copyright>Copyright 2011, NUIORITY.</copyright> <id>com.justinimhoff.kinect.extensions.KinectExtension</id> <versionNumber>1</versionNumber> <platforms> <platform name="Windows-x86"> <applicationDeployment> <nativeLibrary>KinectExtension.dll</nativeLibrary> <initializer>ExtensionInitializer</initializer> <finalizer>ExtensionFinalizer</finalizer> </applicationDeployment> </platform> </platforms> </extension>
Flex Image Uploader
Basic utility that allows the ability to open an image and re-size that image to a specific width or you can use height. Then when requesting the image for an mx:image component it will always be in a png because it takes an internal snapshot. This helps for not saving transparency, reduce file size, protecting against image code embedding, and also allows saving consistent byte array to db if so wanted.
package com.justinimhoff.library.managers { import flash.display.Loader; import flash.events.Event; import flash.events.EventDispatcher; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; import mx.graphics.ImageSnapshot; import mx.graphics.codec.PNGEncoder; /** * Dispatched when the user has picked and image and it is has processed and resized */ [Event(name = "imageReady", type = "flash.events.Event")] /** * Class that can upload an image and directly sets information and scale to * resize an image. When returning an image returns a snap shot where the data * can be used to set an iimage source and file information can be used for the * server */ public class ImageUploader extends EventDispatcher { public static const IMAGE_READY_EVENT:String = 'imageReady'; /** * @private */ private var _image:SmoothImage; /** * @private */ private var _resizeHeight:Number = 60; /** * @private */ private var fileRef:FileReference; /** * @private */ private var fileTypes:Array; /** * Call contructor * @param image Takes a SmoothImage component that is best used when resizing */ public function ImageUploader(image:SmoothImage):void { super(); onConstructor(image); } /** * Displays a pop for the user to select the file they want to upload */ public function browse():void { fileRef.browse(fileTypes); } /** * */ public function get resizeHeight():Number { return _resizeHeight; } /** * Sets the width used to calculate the height and width of the thumbnail from * the original * @private */ public function set resizeHeight(value:Number):void { _resizeHeight = value; } /** * Returns a snapshot to access data and file type for the encoder * that was used - default to png * @return * */ public function get image():ByteArray { var pngEnco:PNGEncoder = new PNGEncoder(); var imageSnapshot:ImageSnapshot = ImageSnapshot.captureImage(_image, 0, pngEnco); return imageSnapshot.data; } /** * * */ public function removeImage():void { _image.source = null; _image.width = 0; _image.height = 0; } /** * Initialize and set listners and define available types allowed. * Only support jpg and png images */ protected function onConstructor(image:SmoothImage):void { //Setup file reference fileRef = new FileReference(); fileRef.addEventListener(Event.SELECT, onFileSelect); fileRef.addEventListener(Event.COMPLETE, onFileLoadComplete); //create filter fileTypes = [ new FileFilter("Images", "*.jpeg;*.jpg;*.png") ]; //Set image _image = image; } /** * Load the image for local manipulation * @param evt */ protected function onFileSelect(evt:Event):void { fileRef.load(); } /** * Load the full image in order to access the height and width of * the original image * @param evt */ protected function onFileLoadComplete(evt:Event):void { //Get size var ldr:Loader = new Loader(); ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadImageComplete); ldr.loadBytes(fileRef.data); } /** * Resize the image directly to create the thumbnail. Uses width only and * calculates the height based on the ratio. Sets the source on the image and sets * the new width and height * @param evt * */ protected function onLoadImageComplete(evt:Event):void { var h:int = resizeHeight; var ratio:Number = h / evt.currentTarget.height; var w:int = evt.currentTarget.width * ratio; _image.height = h; _image.width = w; _image.source = fileRef.data; dispatchEvent(new Event(IMAGE_READY_EVENT, true)); } } }
The smooth image is used for re-sized images and just helps when taking a snapshot.
package com.justinimhoff.library.managers { import flash.display.Bitmap; import flash.display.Loader; import flash.events.Event; import mx.controls.Image; import mx.core.mx_internal; use namespace mx_internal; /** * Image component used for resizing. Available in spark, but not in mx. * TODO - use spark image */ public class SmoothImage extends Image { public function SmoothImage():void{ super(); onConstructor(); } protected function onConstructor():void{ cacheAsBitmap = true; } override public function set source(value:Object):void{ if(value != source){ super.source = value; } } /** * Turn on smoothing as a default * @private */ mx_internal override function contentLoaderInfo_completeEventHandler(event:Event):void { var smoothLoader:Loader = event.target.loader as Loader; var smoothImage:Bitmap = smoothLoader.content as Bitmap; if (smoothImage) { smoothImage.smoothing = true; } super.contentLoaderInfo_completeEventHandler(event); } }
Data Management Utility
This is a management class I like to use in enterprise applications to manipulate common data. It is usually a good design if using BlazeDS to turn on legacy collections so collections will be returned as an Array instead of an ArrayCollection. This will minimize processing and unless you are binding directly to the result, there is no reason to use an ArrayCollection that is dispatching events. These methods help in memory and performance management.
package com.justinimhoff.library.managers { import flash.net.ObjectEncoding; import flash.net.registerClassAlias; import flash.utils.ByteArray; import flash.utils.describeType; import mx.collections.ArrayCollection; import mx.collections.ListCollectionView; import mx.collections.Sort; import mx.collections.SortField; import mx.utils.ObjectUtil; /** * * The DataCollectionManager class is a utility class that allows for the management of * Array, ArrayCollection, and Object. In cases where items need to be copied or cloned from multiple * items, it is better to use this class because it impliments best practices for looping * and event management. * * @see mx.collections.ArrayCollection * */ public class DataCollectionManager extends Singleton { /** * Copies objects from one array to another and is left to the user to manage refresh or events if using source of ArrayCollection. * * @param sourceCollection The collection of all the inforamation that needs to be copied * @param targetCollection The collection to add to * */ public function copyItemsToCollection(sourceCollection:Array, targetCollection:Array):void { var i:int = 0; var len:int = sourceCollection.length; for (; i < len; i++) { targetCollection.push(sourceCollection[i]); } } /** * Removes objects from a targeted collection. The items must be of the same object memory allocation * * @param itemsToRemove The Array of items to be removed * @param targetCollection The collection of where the items reside * */ public function removeItemsFromCollection(itemsToRemove:Array, targetCollection:Array):void { var i:int = 0; var len:int = itemsToRemove.length; var itemIndex:int; for (; i < len; i++) { itemIndex = targetCollection.indexOf(itemsToRemove[i]); if (itemIndex < targetCollection.length + 1) { targetCollection.splice(itemIndex, 1); } } } /** * Clones source of an ArrayCollection or Array into a target collection and manages the event lifecycle by dispatching one update. * * @param sourceCollection The collection of all the inforamation that needs to be copied * @param targetCollection The collection to add to * */ public function cloneArrayToArrayCollection(sourceCollection:Array, targetCollection:ArrayCollection):void { targetCollection.disableAutoUpdate(); targetCollection.removeAll(); targetCollection.source = sourceCollection; targetCollection.enableAutoUpdate(); targetCollection.refresh(); } /** * Clones source of an ArrayCollection or Array into a target collection and cast the object by the provided type. * Also manages the event lifecycle by dispatching one update. * * @param sourceCollection The collection of all the inforamation that needs to be copied * @param targetCollection The collection to add to * @param classType The class to case each object as * */ public function cloneArrayToArrayCollectionClassType(sourceCollection:Array, targetCollection:ArrayCollection, classType:Class):void { targetCollection.disableAutoUpdate(); targetCollection.removeAll(); var i:int = 0; var len:int = sourceCollection.length; for (; i < len; i++) { try { targetCollection.addItem(sourceCollection[i] as classType); } catch (err:Error) { trace('An error has occured in casting the object: ' + String(err.message)); } } targetCollection.enableAutoUpdate(); targetCollection.refresh(); } /** * Converts a vector object to array for easy handling and also for instances where * switchng between mxml and spark components selected items. * * @param v The vector to copy into an Array * @return An array of items that where part of the vector * */ public function convertVectorToArray(vector:Object):Array { var vec:Vector.<Object> = Vector.<Object>(vector); var arr:Array = []; for each (var i:Object in vec) { arr.push(i); } return arr; } /** * Recurively flatten nested objects and keep the property identifier and value of the property * * @param source The object that has information to copy * @param pushTo The clean object to push properties and value into * */ public function pushToFlatObject(source:Object, pushTo:Object):void { var objectInfo:Object; var i:int; var len:int; for each (var item:* in source) { if (ObjectUtil.isSimple(item)) { objectInfo = ObjectUtil.getClassInfo(source); i = 0; len = objectInfo.properties.length; for (; i < len; i++) { //Show the name and the value if (source[objectInfo.properties[i].localName] == item) { pushTo[objectInfo.properties[i].localName] = item; } } } else { pushToFlatObject(item, pushTo); } } } /** * Converts a plain object to be an instance of the class * passed as the second variable. This is not a recursive funtion * and will only work for the first level of nesting. When you have * deeply nested objects, you first need to convert the nested * objects to class instances, and then convert the top level object. * * @param object The plain object that should be converted * @param clazz The type to convert the object to */ public function objectToInstance(object:Object, clazz:Class):* { var bytes:ByteArray = new ByteArray(); bytes.objectEncoding = ObjectEncoding.AMF0; // Write out the bytes of the original object var objBytes:ByteArray = new ByteArray(); objBytes.objectEncoding = ObjectEncoding.AMF0; objBytes.writeObject(object); // Register all of the classes so they can be decoded via AMF var typeInfo:XML = describeType(clazz); var fullyQualifiedName:String = typeInfo.@name.toString().replace(/::/, "."); registerClassAlias(fullyQualifiedName, clazz); // Write the new object information starting with the class information var len:int = fullyQualifiedName.length; bytes.writeByte(0x10); // 0x10 is AMF0 for "typed object (class instance)" bytes.writeUTF(fullyQualifiedName); // After the class name is set up, write the rest of the object bytes.writeBytes(objBytes, 1); // Read in the object with the class property added and return that bytes.position = 0; var result:* = bytes.readObject(); return result; } /** * Copies properties from one object to another * * @param copyTo The object to copy to * @param copyFrom The object to copy from = the source * */ public function copyPropertiesToObject(copyTo:Object, copyFrom:Object):void { var objectInfo:Object = ObjectUtil.getClassInfo(copyFrom); var i:int = 0; var len:int = objectInfo.properties.length; for (; i < len; i++) { //Show the name and the value if (copyTo.hasOwnProperty([ objectInfo.properties[i].localName ])) { copyTo[objectInfo.properties[i].localName] = copyFrom[objectInfo.properties[i].localName]; } } } /** * Allows the creation of multiple complex sorting for a provided collection. Provides the ability to customize the type of sort * and provides support for multiple sort fields. * * @param collection Supports Array and ListCollectionView. * @param sortItems An array of item identifiers to sort against, should be in an array of strings. * @param caseInsensitive When sorting strings, tells the comparitor whether to ignore the case of the values. * @param descending Tells the comparator whether to arrange items in descending order. * @param numeric Tells the comparitor whether to compare sort items as numbers, instead of alphabetically. * */ public function createSort(collection:*, sortItems:Array,caseInsensitive:Boolean=false,descending:Boolean=false,numeric:Object=null):void{ var newSortField:SortField; var sortFieldList:Array = []; //Create sort fields for each(var sortItem:String in sortItems){ newSortField = new SortField(sortItem,caseInsensitive,descending,numeric); sortFieldList.push(newSortField); } //Create sort and assign to correct dataprovider var collectionSort:Sort = new Sort(); collectionSort.fields = sortFieldList; if(collection is Array){ collection.sort = collectionSort; }else if(collection is ListCollectionView){ collection.sort = collectionSort; collection.refresh(); } } } }
Staggered Event Dispatching
When using flex/flash and leveraging remote objects, there will be a time where you might need to dispatch two or more events simultaneous. The issue with this is when using BlazeDS you will run into a bundling effect of the events. The events will be fired in the same frame and as such they will be considered as a bundled request. The down side is that one request will affect the time of return for the other request.
Example:
Request 1 takes 135ms
Request 2 takes 784ms
These request will be processed as a bundle and will not be returned until both are performed so you will not benefit from performances tuned request and your result will be both returning in 919ms and your UI will have to process both pieces of information together instead of staggering layout.
The alternative is to make sure each event is dispatched in a separate frame so there is no bundling.
package com.justinimhoff.library.utilities { import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.utils.Timer; /** * * Creates a queue of events that only one is dispatched every other key frame in order to * get away from bundling of events from the player an browser. When bundling of events occurs, * the slowest running will impac the result time of all others and items live on a single thread * server side. * */ public class StaggeredRemoteDispatcher extends EventDispatcher { /** * set to low value so it will only dispatch on next keyframe * @private */ private var dispatchTimer:Timer = new Timer(100); /** * a queue of events to dispatch * @private */ private var events:Array = []; /** * Adds an event to the queue that will be called on the next frame event. * Starts the time and adds event listner for the next interval * @param event * */ public function addFunction(item:Function):void { if (events.length == 0) { dispatchTimer.addEventListener(TimerEvent.TIMER, dispatchSequenceEvent); dispatchTimer.start(); } events.push(item); } /** * Dispatches the next event in the queue and stops the timer and removes the event * listner if there are no more items. * @param event * */ protected function dispatchSequenceEvent(event:TimerEvent):void { if (events.length > 0) { var callFunction:Function = events[0] as Function; callFunction.call(); //remove just called function events.shift(); } if(events.length == 0){ dispatchTimer.stop(); dispatchTimer.removeEventListener(TimerEvent.TIMER, dispatchSequenceEvent); } } } }
Favorite Singleton Manager
I use this as the core Singleton management class in most enterprise applications. To use, make sure your new class extends Singleton and then in the view grab a instance of a Singleton management class. Extending the Singleton class allows leveraging event dispatching as if you extended the event dispatcher, but through the Singleton class you can manage the memory of Singletons by removing if not in use. This is a simple core class to use and leverage in your enterprise application.
var viewManager:ViewManager = Singleton.getInstance(ViewManager);
package com.justinimhoff.library.managers { public class ViewManager extends Singleton { } }
package com.justinimhoff.library.managers { import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.utils.Dictionary; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; /** * * General Singleton management class. Extend this class to create * a singleton manager. * */ public class Singleton implements IEventDispatcher { /** * internal dictionary to keep class instances * @private */ private static var instanceDict:Dictionary = new Dictionary(); /** * removes the item from the dictionary for garbage collection * @param managementClass * */ public static function removeInstance(managementClass:Class):void { var instance:Singleton = instanceDict[managementClass]; if (instance != null) { delete instanceDict[managementClass]; } } /** * Get the single instance * @return the single instance * */ public static function getInstance(managementClass:Class):* { var instance:Singleton = instanceDict[managementClass]; if (instance == null) { //create a new instance of class managementClass instance = Singleton(new managementClass()); if (instance == null) { throw("getInstance can only be called for Classes extending Singleton"); } } return instance; } /** * Dispatcher to user in singletons */ protected var dispatcher:EventDispatcher = new EventDispatcher(); /** * * We only want one of these - Singletons are actually more performant than static management classes * */ public function Singleton() { onConstructor(); } /** * Used to interface with internal dispatcher * @param type * @param listener * @param useCapture * @param priority * @param useWeakReference * */ public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); } /** * Used to interface with internal dispatcher * @param type * @param listener * @param useCapture * */ public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void { dispatcher.removeEventListener(type, listener, useCapture); } /** * Used to interface with internal dispatcher * @param event * @return * */ public function dispatchEvent(event:Event):Boolean { return dispatcher.dispatchEvent(event); } /** * Used to interface with internal dispatcher * @param type * @return * */ public function hasEventListener(type:String):Boolean { return dispatcher.hasEventListener(type); } /** * Used to interface with internal dispatcher * @param type * @return * */ public function willTrigger(type:String):Boolean { return dispatcher.willTrigger(type); } /** * * Conceptually private constructor. * Manages the singleton document dictionary. * */ protected function onConstructor():void { var className:String = getQualifiedClassName(this); var managementClass:Class = getDefinitionByName(className) as Class; if (managementClass == Singleton) { throw("Singleton is a base class that cannot be instantiated"); } var instance:Singleton = instanceDict[managementClass]; if (instance != null) { throw("Classes extending Singleton can only be instantiated once by the getInstance method"); } else { instanceDict[managementClass] = this; } } ; } }
Ant + Library SWC + Namespaces
There are many cases where you will build a custom component library and use namespaces, this is what I have found to work the best. Can post step for creating and using a manifest, but there are many links on google. The key to this is using the current Adobe namespaces as not to overwrite and adding your own. When deploying the swc, make sure it is copied back into the libs directory so Flash builder can pick up the changes.
<!-- build component library swc -->
<macrodef name="flex-compile-production-swc">
<attribute name="swc-name" />
<attribute name="locale"
default="en_US" />
<sequential>
<compc output="${source.libs}/library/@{swc-name}${version}.swc"
locale="@{locale}"
maxmemory="768m"
fork="true"
debug="false">
<source-path path-element="${source.flex.dir}" />
<include-sources dir="${source.flex.dir}/com/justinimhoff/library/"
includes="**/*.as **/*.mxml" />
<namespace uri="http://ns.adobe.com/mxml/2009"
manifest="${FLEX_HOME}/frameworks/mxml-2009-manifest.xml" />
<namespace uri="library://ns.adobe.com/flex/spark"
manifest="${FLEX_HOME}/frameworks/spark-manifest.xml" />
<namespace uri="library://ns.adobe.com/flex/mx"
manifest="${FLEX_HOME}/frameworks/mx-manifest.xml" />
<namespace uri="http://www.adobe.com/2006/mxml"
manifest="${FLEX_HOME}/frameworks/mxml-manifest.xml" />
<namespace uri="library://justinimhoff.com/flex/components"
manifest="${source.libs.components}/component-manifest.xml" />
<include-namespaces>library://justinimhoff.com/flex/components</include-namespaces>
<!-- Externally linked Flex libraries -->
<compiler.external-library-path dir="${FLEX_HOME}/frameworks/"
append="true">
<include name="libs/player/playerglobal.swc" />
<include name="libs/framework.swc" />
<include name="libs/spark.swc" />
<include name="libs/sparkskins.swc" />
<include name="libs/utilities.swc" />
<include name="libs/textLayout.swc" />
<include name="libs/rpc.swc" />
<include name="libs/flex.swc" />
<include name="locale/${locale}/framework_rb.swc" />
<include name="locale/${locale}/rpc_rb.swc" />
</compiler.external-library-path>
</compc>
<copy preservelastmodified="true"
todir="${output.dir.rsls}">
<fileset casesensitive="yes"
dir="${source.libs}/library/">
<include name="@{swc-name}${version}.swc" />
</fileset>
</copy>
<echo>Succesfully compiled @{swc-name}</echo>
</sequential>
</macrodef>
Kinect + Street View + Adobe AIR
Quick demonstration of Kinect controlling Google Street View with Adobe AIR:
Controls:
- Rotate body right: rotate image right
- Rotate Body left: rotate image left
- Walk (knee up): progress forward
- Lean Up: look up
- Lean Down: look down
This is a quick demonstration of leveraging the kinect for NUI with Street View. The example above uses AS3OpenNI (shout out) with Google Street View in Adobe AIR.
Adobe Flex versioning with ant and cache control
So you are building a enterprise Flex application that uses automated builds with ant. You are running into the issue of multiple modules and assets that use the same name but you want to identify the swf by the specific version of the build and maybe even deploy multiple builds in the same structure allowing them to share un- changed resources. Or just maybe you want to make sure that when you deploy a new production build, that the swfs are not cached. The below code will help with both scenarios. As a quick background, when using RSLs, the RSL contains a digest or UID that when the application is compiled, it is compiled against that digest. When requesting RSLs, the application request RSLs by name but then also validates the digest against what the application was compiled against to ensure it is using the latest / correct library.
/** * property in the build file that identifies the version number used to build swfs * @private */ private static const BUILD_PROPERTY_VERSION_IDENTIFIER:String = 'version'; /** * The regular expression that only allows numbers, dashes, and period from the build * property string * @private */ private static const BUILD_PROPERTY_VERSION_REG_EXP:RegExp = /[^0-9-.]/g; [Embed(source="../build/build.properties", mimeType="application/octet-stream")] /** * Embedded build property file */ private static const embeddedBuildFile:Class; /** * With searching if an item is not found it return -1, so anything 0 or greater means the * item has been found at least once in the string. */ private static const ITEM_FOUND:int = 0; /** * The version of the application * @private */ private var _appVersion:String = ''; /** * If the property is not already set, loop through all build properties that have been embedded * when compiled until the property contains an identifier related to the build version. Once found, * clean up any characters that are not of a build number and set property * @return build version number * */ public function get version():String { if (_appVersion == '') { // Create a new class of the embedd type and cast as a string to initialize var buildProperties:String = String(new embeddedBuildFile()); // Split all line breaks into an array var arr:Array = buildProperties.split("\n"); var i:int = 0; var len:int = arr.length; var buildProperty:String; // Loop through all poperties untile the identifier is found and set for (; i < len; i++) { buildProperty = arr[i]; if (buildProperty.search(BUILD_PROPERTY_VERSION_IDENTIFIER) >= ITEM_FOUND) { _appVersion = buildProperty.replace(BUILD_PROPERTY_VERSION_REG_EXP, ''); break; } } } return _appVersion; }
We want to essentially copy this same structure while also promote caching when possible. In most build scenarios you are versioning the application and maybe using automated incremental build numbers. This is a great thing and something we want to take advance of, so the first step is to embedded the build.properties file (properties.xml would require parsing as XML) so we have the incremented version or build number that the swf is being built as. When embedding the properties file, you can try decompiling the swf, but you will not be able to read out the file – only during runtime initialization will the values be available. This may be a great thing if you are using encryption keys in the application. So the file has the latest build so then the next step just comes to instantiating and looping through the values until the expected property is found and then just clean up the property values based on your versioning scheme.
The next step to this is to build out your applications, modules, and runtime assets with the build version appended. Example; Application.1.2.3-45678.swf or module.1.2.3-45678.swf. This allows a visual identifier of the build version and ability to deploy multiple applications of different builds to the same directory. Now the real reason we have embedded the version in the application is we need to know the URL for the assets or the modules – we know the identifier but need to append the version. This will allow an application to only load the correct module or asset swf for the specific build. This example can also be extended to custom libraries or even as RSLs.
The other benefit to this approach is caching, when building Flex application we have all run into informing the client to clear there cache and try again – but no more. In this scenario the application.html page will be requesting a completely different swf file because it will be referencing the swf with the appended build, so it will always be fresh – but not just that, we also will be leveraging the caching of the swf in the browser. If the application.html file is pointing to the same swf consecutively it will use the cache of the swf and you will no worry about the client having the latest and greatest. You can use this in a singleton or static through-ought the application if there are other properties that might be worth noting like server build time . Can definitely go into more detail but hopefully this can help other out and is a good start.
Flatten nested Objects in Flex
This might seem like a small thing, but when you are requesting results back from the Google Maps API for example, and are presented with dynamic nested objects, your validation can be daunting. This simple function will allow you to pass in the object to be flattened into a blank object or an object you would like to append to. This will allow simple validation of the object, and the ability to convert to a strict type object if necessary. This was used in a singleton utility management class, if to be used in a static, add static and recursively call the class.
Simple = Yes / Time Saver = Hells Yes!
public function pushToFlatObject(source:Object, pushTo:Object):void { for each (var item:* in source) { if (ObjectUtil.isSimple(item)) { var objectInfo:Object = ObjectUtil.getClassInfo(source); var i:int = 0; var len:int = objectInfo.properties.length; for (; i < len; i++) { //Show the name and the value if (source[objectInfo.properties[i].localName] == item) { pushTo[objectInfo.properties[i].localName] = item; } } } else { pushToFlatObject(item, pushTo); } } }
Flex 4 and the AdvancedDataGrid Error
If you are using Flex 4 or even Flex 3 with the AdvancedDataGrid and you run into this error:
TypeError: Error #1007:Instantiation attempted on a non-constructor.at mx.controls::AdvancedDataGridBaseEx/getSeparator()
This is caused by the default css for the flex datavisualization library not including styles and class references that are required in the Advanced Data Grid. You can either use the below code in your main application css code, or go to Flex SDK\frameworks\projects\datavisualization and compile the data_management.css into a swf and load it at runtime – this is a better choice if using modules. This error has been around since Flex 3 and if you try and compile the swc by changing the css, you will loose the swz and the ability to cache in flash player.
Fix (add to CSS, this is the basics required for the most part ):
mx|AdvancedDataGrid{ columnDropIndicatorSkin: ClassReference("mx.skins.halo.DataGridColumnDropIndicator"); columnResizeSkin: ClassReference("mx.skins.halo.DataGridColumnResizeSkin"); headerColors: #FFFFFF, #E6E6E6; headerDragProxyStyleName: "headerDragProxyStyle"; headerBackgroundSkin: ClassReference("mx.skins.halo.DataGridHeaderBackgroundSkin"); headerSeparatorSkin: ClassReference("mx.skins.halo.DataGridHeaderSeparator"); headerHorizontalSeparatorSkin: ClassReference("mx.skins.halo.AdvancedDataGridHeaderHorizontalSeparator"); headerStyleName: "advancedDataGridStyles"; sortArrowSkin: ClassReference("mx.skins.halo.DataGridSortArrow"); stretchCursor: Embed(source="Assets.swf",symbol="cursorStretch"); } mx|AdvancedDataGridSortItemRenderer{ paddingTop: 0; paddingBottom: 0; paddingLeft: 0; paddingRight: 0; horizontalGap: 0; color: #0B333C; icon: ClassReference("mx.skins.halo.DataGridSortArrow"); } mx|PrintAdvancedDataGrid{ alternatingItemColors: #FFFFFF, #FFFFFF; borderColor: 0; columnResizeSkin: ClassReference("mx.skins.halo.DataGridColumnResizeSkin"); headerColors: #FFFFFF, #FFFFFF; headerSeparatorSkin: ClassReference("mx.skins.halo.DataGridHeaderSeparator"); headerStyleName: "advancedDataGridStyles"; horizontalGridLineColor: 0; horizontalGridLines: true; sortArrowSkin: ClassReference("mx.skins.halo.DataGridSortArrow"); stretchCursor: Embed(source="Assets.swf",symbol="cursorStretch"); verticalGridLineColor: #000000; } mx|PrintOLAPDataGrid{ alternatingItemColors: #FFFFFF, #FFFFFF; borderColor: 0; columnResizeSkin: ClassReference("mx.skins.halo.DataGridColumnResizeSkin"); headerColors: #FFFFFF, #FFFFFF; headerSeparatorSkin: ClassReference("mx.skins.halo.DataGridHeaderSeparator"); headerStyleName: "advancedDataGridStyles"; horizontalGridLineColor: 0; horizontalGridLines: true; stretchCursor: Embed(source="Assets.swf",symbol="cursorStretch"); verticalGridLineColor: #000000; }
Swipe Gesture with UIWebView
This is something that had me banging my head against the wall and thought I would share. The use case is using the left and right swipe gesture to register against the UIWebView/UIView that then calls a swipe function. For my case I was using it to change navigation on a UITableView, but you could use is for back/forward on the UIWebView itself or use the same process to register custom gestures. Of all the resources I found, they use a UIView over the UIWebView, or a disable interactivity on the UIWebView and then push touch event into it. The code is used inside a UIViewController with a UIWebVIew present.
- (void)viewDidLoad { [super viewDidLoad]; UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightAction:)]; swipeRight.direction = UISwipeGestureRecognizerDirectionRight; swipeRight.delegate = self; [webView addGestureRecognizer:swipeRight]; UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeftAction:)]; swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft; swipeLeft.delegate = self; [webView addGestureRecognizer:swipeLeft]; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } - (void)swipeRightAction:(id)ignored { NSLog(@"Swipe Right"); //add Function } - (void)swipeLeftAction:(id)ignored { NSLog(@"Swipe Left"); //add Function }
Cairngorm 3 / parsley|spicelib – RSL
Cairngorm 3 is really just parsley and spicelib with utility libraries that help development and code reuse. Recently on a modular application I wanted to convert the libraries to RSLs, for the benefits of multiple modules leveraging the library without having to compile. This is all good until you actually set the modules to compile with reference to the RSL in the ant environment. I ran into various errors of dependency which i could not figure out for hte life of me until I got down and dirty with the libraries. The order they are associated in will fix the dependency. This may seem simple because if you use Flash builder everything works great, but when using ant it changes dramatically and it is up to the developer to define the build. So hopefully this will make someones life easier.
Runtime Shared Library implimentation in ant:
<!-- ORDER IS IMPORTANT::DONOT CHANGE --> <!-- Reflection api --> <runtime-shared-library-path> <path-element>${lib-dir}/spicelib_flex_2.2.2.swc</path-element> <rsl-url>${rsl-url}/spicelib_flex_2.2.2.swf</rsl-url> <policy-file-url>${crossdomain-url}</policy-file-url> <rsl-url>${rsl-url}/spicelib_flex_2.2.2.swf</rsl-url> <policy-file-url> </policy-file-url> </runtime-shared-library-path> <!-- IOC framework for use with metadata --> <runtime-shared-library-path> <path-element>${lib-dir}/parsley_flex4_2.2.2.swc</path-element> <rsl-url>${rsl-url}rsls/parsley_flex4_2.2.2.swf</rsl-url> <policy-file-url>${crossdomain-url}</policy-file-url> <rsl-url>${rsl-url}/parsley_flex4_2.2.2.swf</rsl-url> <policy-file-url> </policy-file-url> </runtime-shared-library-path> <!-- Observe other objects and react to changes in some way and execute view behaviour --> <runtime-shared-library-path> <path-element>${lib-dir}/observerParsley_1.8.swc</path-element> <rsl-url>${rsl-url}rsls/observerParsley_1.8.swf</rsl-url> <policy-file-url>${crossdomain-url}</policy-file-url> <rsl-url>${rsl-url}/observerParsley_1.8.swf</rsl-url> <policy-file-url> </policy-file-url> </runtime-shared-library-path> <!-- Simplifies the configuration, rendering and loading of modular content --> <runtime-shared-library-path> <path-element>${lib-dir}/module_0.9.swc</path-element> <rsl-url>${rsl-url}rsls/module_0.9.swf</rsl-url> <policy-file-url>${crossdomain-url}</policy-file-url> <rsl-url>${rsl-url}/module_0.9.swf</rsl-url> <policy-file-url> </policy-file-url> </runtime-shared-library-path> <!-- Provides the ability to group validators --> <runtime-shared-library-path> <path-element>${lib-dir}/validation_1.9.swc</path-element> <rsl-url>${rsl-url}rsls/validation_1.9.swf</rsl-url> <policy-file-url>${crossdomain-url}</policy-file-url> <rsl-url>${rsl-url}/validation_1.9.swf</rsl-url> <policy-file-url> </policy-file-url> </runtime-shared-library-path>
You will also want to create the RSL swf from the swc in ant. Make note the the digest tool. This is extremely import in most cases will be a large issue. The digest is what is used to identify the correct library that the application was compiled against.:
<!-- Build out rsls library --> <macrodef name="create-rsl"> <attribute name="rsl-dir" /> <attribute name="swc-dir" /> <attribute name="swc-name" /> <sequential> <unzip src="@{swc-dir}/@{swc-name}.swc" dest="@{rsl-dir}"> <patternset> <include name="library.swf" /> </patternset> </unzip> <!-- optimize - I use a library config that sets debug false and opt to true, also include metadata as shown below. You can also default to the flex-config.xml --> <exec executable="${FLEX_HOME}/bin/optimizer.exe" failonerror="true"> <arg line="-input @{rsl-dir}/library.swf" /> <arg line="-output @{rsl-dir}/@{swc-name}.swf" /> <arg line="-load-config configs/library-config.xml" /> </exec> <!-- most important part that is not completely documented --> <delete file="@{rsl-dir}/library.swf" /> <exec executable="${FLEX_HOME}/bin/digest.exe" failonerror="true"> <arg line="-digest.rsl-file @{rsl-dir}/@{swc-name}.swf" /> <arg line="-digest.swc-path @{swc-dir}/@{swc-name}.swc" /> </exec> <!-- copies the swf rsl back into the directory for rsl deployment without rebuilding each time --> <copy overwrite="true" preservelastmodified="true" todir="${source.libs}/rsls/"> <fileset casesensitive="yes" dir="@{rsl-dir}/"> <include name="@{swc-name}.swf" /> </fileset> </copy> </sequential> </macrodef> ##USE## <create-rsl rsl-dir="${output.dir.rsls}" swc-dir="${source.libs}/library/" swc-name="componentLibrary" />
Another key to using RSLs that leverage the parsley metadata is to make sure when you compile that you are keeping the metadata:
<keep-as3-metadata> <name>Metadata</name> <name>DefaultProperty</name> <name>Required</name> <name>Event</name> <name>AssignableTo</name> <name>Inject</name> <name>InjectConstructor</name> <name>Factory</name> <name>Init</name> <name>Destroy</name> <name>Observe</name> <name>AsyncInit</name> <name>ManagedEvents</name> <name>MessageDispatcher</name> <name>MessageHandler</name> <name>MessageBinding</name> <name>MessageInterceptor</name> <name>MessageError</name> <name>Command</name> <name>CommandResult</name> <name>CommandError</name> <name>CommandStatus</name> <name>ResourceBinding</name> <name>Selector</name> <name>Target</name> <name>Internal</name> <name>ObjectDefinition</name> </keep-as3-metadata>
Flash Multitouch Gesture Example
Here is a quick example of using the build in TransformGestureEvent generated from Flash 10.1 for use on a multitouch supported device. I am going to keep this post short and explain the good and the bad, but a follow up post will use TouchEvents to generate custom gestures that will allow a flow from one gesture to another and also support for multiple gestures at once. You have two options when implementing multitouch in Flash, either through the TouchEvent itself, or through TransformGestureEvents that Flash generates from TouchEvents. The bad is you can have both. At this time, Flash only support one or the other due to the capture used to generate the gesture events internally in Flash. A monster downside to this approach is only one TransformGestureEvent is supported at a time. You will see that if you implement this component, you must release a gesture event, such as a touch to then go into a rotate. As stated above the next post will create custom gesture events in hopes of cloning the Microsoft Surface Collage in Flex. This is to give an basic intro into what it takes to make a simple Gesture based component – if you really want to have fun, download Google maps for flash and add a zoom in / zoom out on the Gesture Zoom Event. Its great that Flash supports gesture events out of the box and the hope is that it will develop as the community develops towards a more multitouch aware development pattern.
package com.justinimhoff.multitouch { import flash.events.MouseEvent; import flash.events.TransformGestureEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.ui.Multitouch; import mx.controls.Image; public class GestureImage extends Image { private var offsetX:Number; private var offsetY:Number; public function GestureImage() { if(Multitouch.supportsGestureEvents){ this.addEventListener(MouseEvent.MOUSE_DOWN, startDragging); this.addEventListener(MouseEvent.MOUSE_UP, stopDragging); this.addEventListener(TransformGestureEvent.GESTURE_ROTATE, onGestureRotate); this.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onGesturePinch); } } private function startDragging(event:MouseEvent):void { setAsCurrentChild(); offsetX = event.stageX - this.x; offsetY = event.stageY - this.y; stage.addEventListener(MouseEvent.MOUSE_MOVE, moveImage); } private function stopDragging(event:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveImage); } private function moveImage(event:MouseEvent):void { this.x = event.stageX - offsetX; this.y = event.stageY - offsetY; event.updateAfterEvent(); } private function onGesturePinch(pinchEvent:TransformGestureEvent):void{ setAsCurrentChild(); var pinchMatrix:Matrix = this.transform.matrix; var pinchPoint:Point = pinchMatrix.transformPoint( new Point((this.width/2), (this.height/2))); pinchMatrix.translate(-pinchPoint.x, -pinchPoint.y); pinchMatrix.scale(pinchEvent.scaleX, pinchEvent.scaleY); pinchMatrix.translate(pinchPoint.x, pinchPoint.y); this.transform.matrix = pinchMatrix; } private function onGestureRotate(rotateEvent:TransformGestureEvent):void { setAsCurrentChild(); var rotateMatrix:Matrix = this.transform.matrix; var rotatePoint:Point = rotateMatrix.transformPoint( new Point((this.width/2), (this.height/2))); rotateMatrix.translate(-rotatePoint.x, -rotatePoint.y); rotateMatrix.rotate(rotateEvent.rotation*(Math.PI/180)); rotateMatrix.translate(rotatePoint.x, rotatePoint.y); this.transform.matrix = rotateMatrix ; } private function setAsCurrentChild():void{ this.parent.setChildIndex( this, this.parent.numChildren-1 ); } } }
The Bullitt at High Plains Raceway
This is a quick video at HPR, but next time hoping to have the camera for some POV racing. Since then, slapped on some new rotors and intake – looking to drop it soon to take on those turns.
Tutorial on using runtime skinning in Adobe AIR applications
There is a new tutorial on The Tech Labs that deals with runtime skinning of Adobe AIR applications and performance considerations. I wrote the tutorial to expose some general best practices of using runtime skinning and also to detail performance considerations. I hinted at the idea of native application skinning, but i wanted to dedicate a blog post to that topics, which follows below. The basic idea behind native application skinning is going beyond service based applications to truly integrated desktop applications that leverages OS specific user experience expectations. When we look at the applications we use day to day, we develop expectations on how it looks, functions, and interacts with us. It becomes easy to tell the difference between a widget and a desktop application. Main factors for integration are color and style, integration of user interaction paradigms per OS, and visual controls and implicit hot keys. Lets look at a couple of chat application to give a visual description of what I mean.

Above we see three applications – Tweetie (Mac client), TweetDeck(AIR client), and Twitterrific(Mac client). When visually describing the application we want to concentrate on how the application provides similar experiences and expectations to other applications we use on our desktop. Tweetie is a fully integrated native application that keeps with Mac UI and UX. The Tweetie application leverages a large header to add user controls such as search, and navigation. Tweetie has a preferences menu and multiple hot keys that can be accessed from the toolbar. Application controls and configuration preferences are basic expectations from the user of an application. When we look at TweetDeck, we see a SWF wrapped in a standard chrome window from AIR. These native window are OS specific, but you loose the ability to leverage the header for user controls and advanced application name management. When using the application, the first time user has to hover over icons to get descriptions, and the hot key are not available on the toolbar. When examining Twitterrific, we have tons of hot keys that are unknown unless you read the documentation. There is no preferences integration and it really can be defined as more of a widget then a desktop application. So we see that the platform on which the application is build does not define how the application meets native integration. We as AIR developer can do anything and are really unlimited in our experiences that we create with our applications. This idea of unlimited design is good and bad, the good is that we can create truly brilliant applications, the bad is that it takes more time and development just to get to the level of integrated native style and expectations.
The idea is that Adobe AIR developers should be creating application like Tweetie that really leverage the native UI and compliment user workflow. It is upon us as developer to leverage the work that Microsoft and Apple have already done in creating user expectation and workflow, and leverage those to create a truly integrated user experience. I can say that the current way we use the computer is not the best, but at the same point it is what we use and unless you are creating something revolutionary, you should create your applications on user expected experiences. The New York Times desktop reader application highlights how an AIR application can be integrated successfully on the desktop:

Adobe did an excellent job in leveraging the header to manage user controls and really create a sweet application name style. The user notification on updating of articles is awesome, the only thing i would like to see change is the window controls which always seem disabled. This shows an enterprise application that meets my expectations as a user. I would like to see hot key support in the toolbar, but this is a great step and example.
I want to see more and more AIR application that are not just widgets, dashboard apps, or SWFs wrapped in native chrome, but instead are an extension of the native OS that is a fully functional and integrated kick ass application. I have been working on a skin library to support OSX, XP, and Vista in Flash. Each has its own challenges, but i am looking so see if what i am talking about above makes sense, or if i am just a crack pot. If you are interested, drop me a line or create a rebuttal that tells me i do not know what i am talking about – I look forward to the challenge.
What Twitter means to Business
“So what are you doing” is the basic definition of Twitter interaction. It allows people to communicate information that is in-between emails, blogs, and phone calls. Its that spree of the moment “I need to tell you something”. These information posts are shared to those who have chosen to take interest, known as followers, who are interested in the information in-between. So lets step back and look objectively at this: Twitter provides the ability for users to make miniature information posts about issues pertaining to location, likes and dislikes, experiences, wants / needs, movement, and any other possible detail concerning the posters life or thoughts on it. In most cases this can seem rather trivial and unimportant compared to mainstream news or fully qualified blog posts with comments. But what is really not understood is the complexity of Twitter and what value it holds for the business community in general. I am going to go through some basic of Twitter and then explorer its exploited value, or at least give some good ideas.
Twitter not only allows for people to communicate, but make smart communication. Twitter uses Hashtags to embedded additional context and metadata in tweets. The hashtags provide a way of grouping posts to a main subject that is searchable. An example of a subject would be #Microsoft. Go to http://search.twitter.com and do a search for #Microsoft and then Microsoft. Your results will differ in the actual content and the information from this is truly valuable. Understanding that those using Microsoft as a subject are more oriented to using it as an everyday term, compared to those using it as a word to describe a current situation or irritation. Try using #free to find people giving away furniture, tickets, or just about anything. For more information on hashtags and how to use them. Twitter also allows users to do direct remarks to other users through the user of @twittername instantiation. This is similar to the ability to comment on Blogs, witch allows for discussion or what we will talk about in later with business interactions with Twitter.
So we have seen some of the basic features of Twitter that allow for more derived information collection from the metadata of a post, but we also need to understand the value of a post. Think of a Twitter user as a mainstream news source on TV. TV news media value is based on Nielsen viewer ratings for how many house holds watch a certain program. This allows the news media to charge higher rates for advertisement time. The same can be said for Twitter followers. Followers are users who have specified that they find a person’s post intrinsically valuable. So for today the most followed is CNN and Britney Spears. With Britney having a total of 873,333 followers, that is not much less than her total Blackout album sales as of January 2009 (942,000 copies in the United States). These followers are people who want to be updated on the newest CNN stories by phone, computer, and other Twitter communication clients. The system of following is no different than consuming an RSS feed, receiving weekly group emails, or blog alerts / searches. Twitter truly allows a single person or entity create a fast and simple communication medium to interact with those interested – you though setting up a blog was easy, try signing up for a Twitter account – WOW!.
Now lets get into the heart of this post – business value. In business you learn that the customer is the most valued resource and is truly the one you must answer too. How better to communicate and interact with the customer than by really knowing and understanding the customer. Use Twitter as a way to find what the customer is interested in, what is going on in their life, and who they follow or who follows them. People are more open to posting personal information on Twitter than in any phone survey or interview / group discussion. Just the basics of finding out if the customer is married, has children, and location without asking are some of the most valuable sets of data will allow customer integration and acceptance. Use search.twitter to search your company name or product for feedback and usage information. Use an aggregator like Filtrbox.com to help you manage multiple searches and analyze the results of posts. You can aggregate who liked / disliked the product and perform a follow up in Twitter by sending them a direct message on how you can better serve them or even give a quick thanks if they provided positive feedback about the business or product, this always helps. Use hybrid application like Brightkite.com to not only discover posts, but the location the post took place in. Posts like: “I was at Target the other day and they where out of Crest Whitening toothpaste, damn”. You can know discern from the location which store this post was written about and actually check to see if the JIT inventory system is working or if there are other issues with the store – is this the only person complaining about the store or the stocking of the store?
Twitter gives a business the ability to micromanage a customers needs and wants by managing how they interact passively or directly with a customer. Reiterate, i am using customer compared to consumer for the specific reason of the ability to hone in on a person, not a group people. Want people to take a survey, just post a #free ipod when you take this survey in Twitter. Want to target a certain group of people, just look at who is commenting on a specific subject like #baseball - you now have a list of people who are interacting in discussion about your topic of choice. You now have a viral feature that you company can leverage – stack this up with Facebook integration and you have a new market and marketing / sales integration points that are instantly global. With over 1 million users, it would be wrong for you business to not out reach. I know that there are a lot more business cases and examples than this, but these should be rather simple integration idea that your business can active and expect results from.
Future of the Tweet. Be ware, this is a total opinion - Twitter and Brightkite are just simple stepping stones to the future of a new medium for communication and interaction. Twitter is no different from a txt message that is received by a group of people, but what i do see is the advanced ability to leverage metadata and interact at a public level that you could not do before unless you had a blog or were a journalist. I hope to see a hybrid in educational institutions that use it for discussions and information gathering - why read an article when you can ask the CEO and write your own (overly stated example).
Now what not to do if you are a Business. The problem with integrating business and Twitter is the actual loss of value to a business and to Twitter users as they become more integrated. In most cases, when people post objections to a business or create their own product reviews, they are creating posts from personal opinions. Some unnamed business have decided to use people with lots of followers to write pleasing product reviews for a price. This is truly advertising in social networks and really inhibits the environment of objectivity in my personal opinion. Another thing to stay away from if you really are a socially understanding company is using Tweets as a news source or a documented product reviews. Remember, Tweets are personal opinions that are shared global, but not with intent to be published as a valid review that a company should use to endorse products. When a user endorses your product, follow the user and interact with them learning more and then gain a endorsement that is concentrated and well received across multiple audiences. For example, an Angelina Jolie tweet of “I love season 7 of Friends” would not really be a great endorsement for Jennifer Aniston and Friends. So please remember that even thought social networks are rather new – they are really another implementation of any other content source and should be treated as such – please don’t bombard Twitter with advertisement, i actually like using it.
UXI – User Experience Interface
So being a developer and a designer I have recently come to the conclusion that chances are some one has done it before and some one has used it before. What this means is that many of us know the websites we like, the tools we use, and the interactions we expect. The development community has built thousands of paradigms that encapsulate user needs and provide distinct user patterns. We have compiled a knowledge base on our users over the years in order to develop better, stronger, faster applications. Some companies have a UX team and a UI team – treating each as a separate entity. By todays definition, the UX is based on the UI and the experience that the UI provides to the user. When i think of this i wonder – does the user not already have an expectation of their experience before they even begin? Are these expectations not in line with other user’s expectations and how users may experience other applications? I understand that RIA is a new way to experience web applications, with that expectation that the browser would be similar to expectations from the desktop.
Due to these questions i am trying to discern a new development strategy that incorporates what we already know about the UX and build the UI off of that, not vice versa. It seems like a term for now and really is a simple hybrid, but i would like to develop a framework off of this that implements the strategy of using predefined interactions and expected movement / results as the prime motivation for how an application is designed and developed – the interactions are there, now lets build the logic. I would like to see Flash Catalyst as a first step, but i think this also has to do with the company, developer mindsets, and development practices. When designing in UX and UI , we understand that we are providing an application where the user expects (1-2-3-4) and when executing (1-2-3-4) they expect (a-b-c-d). So when the user initiates (a), there may be an expectation that (e-f-g) is ready and instant – an expectation or more from less. This is the ability of defining the design from expectation that follow discernible patterns.
When creating a search application the user expects an input box and a “search” button. When the button is clicked the user expects the input box and button to still be present and the results to load below it. There is no expectation on how the results load (movement of adding or effects), but they expect the results to load rather fast and for the first result to be the most important (key). The results are paginated and there is a total on how many articles or pages are available for the search. These are the expectations of the user and for a good user experience they must be met. Google has taken this one step further by then adding new articles to the topics search for instantly (live) – this is the (e-f-g). We do not develop an experience that is off centered from basic paradigms, nor do we develop interactions that will cause a new experience that is not a current paradigm if the application is not of a predefined sort, unless we educate the user to the advantages and efficiencies. The education of the user to a new experience is key to creating a new expectation that the user can expect from the interface, not the user creating an expectation from using the interface. by following this pattern you can develop a core structure that can be built upon to promote advanced user interactions. This does go against the idea of user trial and error – but for things such as the touch screen integration, the user expects that they use their finger instead of a cursor or keyboard – but they must be educated through advertising, friends, or documentation on how they should expect any other interaction to work – pinch and rotate. This states that a company must develop from predefined expectations unless they are willing to educate and inform. This does not state that all paradigms are correct, most really are not, but they have come to be expectations to a user and should be treated as such.
Filtrbox Fights – Trend Analysis with Humor
At Filtrbox we have a basic trend analysis graph that shows the amount of mentions on a specific topic for a selected number of days. We will be adding more charting features as time passes, but for now i want to look at trend analysis and really envelop what it means. A good description of a trend is a developing direction, whether up or down, that is ever changing. A trend allows us to really analyze a large data set to be able to make inference to what the future may be like. Below is a quick trend of McCain (Brownish), Obama (Blue), and the Olympics (Red).

As we can see, it has been back and forth with Obama and McCain. The Olympics have also followed a similar trend. These trends can inform us that bloggers don’t like to post on Sundays, hype for the Olympics is falling as it moves to opening day – witch it might shoot back up again, and there may be a correlation between Olympics and the presidential elect. Clicking on a point will also allow us to get a better idea for what may have spurred the peak or the valleys. So if we look at August 1 when Obama has a total of 359 mentions, we can communicate that these are mostly responses to McCain’s attack ad and defending Obama’s persona.
This now leads to the title of the post, Filtrbox Fights. A Filtrbox Fight is a basic trend analysis game, that who ever has the most mentions in the day is the winner (This is not supported by Filtrbox and is something i thought would be of interest). I am hoping to keep track of the presidential candidates and see if there is a correlation between who wins and who had the most internet traffic. This does not mean that if Obama had the highest internet traffic and is the next president, that there is a definitive correlation, but we can infer that the internet would be a good place to campaign in, and also which interent sources provided the information – Twitter and other social networks are changing how people interact and react to information. Thanks for reading and hope you try a Filtrbox Fight of your own – very rewarding
E4X to Object to Array and back again
Recently when working on the Filtrbox Olympic AIR widget, I ran into some trouble in reading and writing preference in AIR as XML. There was not very much information concerning the topics so i felt i would give some quick insight. I am sure there are better answers, but this worked for me. This write- up assumes that you already know how to create, and read XML data from AIR, but you are having hard time with tubular data. This app is designed around RSS consumption, so we will create a Value Object called FeedVO that looks like this (Using the Cairngorm Micro Architecture):
package com.filtrbox.widget.vo{ import com.adobe.cairngorm.vo.IValueObject; [Bindable] public class FeedVO implements IValueObject{ public var feed:String; public var name:String; public var type:String; } }
Alright, with the VO setup, its time to read in preferences. First, lets set up a reference to the XML object we will be using, called “applicationSettings”.
[Bindable] public var applicationSettings:XML;
Now lets create the XML scheme that we will use to add data to and create the default structure. This same structure that is also used to process the data.
public function createXMLSchema():Void{ applicationSettings = <preferences/>; var beijing:XML = <beijing/>; for each(var feed:FeedVO in model.allFeeds){ beijing.@name = feed.name; beijing.@feed = feed.feed; beijing.@type = feed.type; applicationSettings.appendChild(beijing); beijing = <beijing/>; } writeXMLData() }
Lets now look over what we have done. We have created two nodes – preferences and beijing. Since we are using E4X, we can use node values like “feed” and “type”. In the model, an Array Collection has been created that will contain all feed information and thus is called “allFeeds”. So for each value object in the “allFeeds” array, we will recurs and append the result to a new beijing node. After the last result has been added, we will want to write the data to the XML file.
private function writeXMLData():void { var outputString:String = '\n'; outputString += applicationSettings.toXMLString(); outputString = outputString.replace(/\n/g, File.lineEnding); stream = new FileStream(); stream.open(applicationFile, FileMode.WRITE); stream.writeUTFBytes(outputString); stream.close(); }
When reading the preferences, we will follow the same structure:
private function processXMLData():void { applicationSettings = XML(stream.readUTFBytes(stream.bytesAvailable)); stream.close(); for each(var beijing in applicationSettings.beijing){ var feed:FeedVO = new FeedVO; feed.name = beijing.@name; feed.feed = beijing.@feed; feed.type = beijing.@type; model.allFeeds.addItem(feed); } if(model.allFeeds.length<=0 || model.allFeeds == null){ var defaultFeed:FeedVO = new FeedVO; defaultFeed.name = 'Olympics 2008'; defaultFeed.type = 'event'; defaultFeed.feed = 'lEpq5xVyDq5UtjtNOboOyQ=='; model.allFeeds.addItem(defaultFeed); } }
When reading the preferences, we will recurs the biejing node and separate each value to add as an object. Once we have created the object, we will add it to our “allFeeds” array in the model. We also want to handle a first time use case where there are no preferences and load a default feed, which is handled by the second “if” statement. So I quickly showed you how to recurs tubular data in order to read or write to and XML file. When you are using the AIR environment, you may also want to use SQLite, which will follow a similar format in how you handle the objects.
Make a mashup your way
Want to have power over your own news content? Well then welcome to the Filtrbox RSS feed. As a Filtrbox member (its a FREE account), you have the ability to create your own RSS feeds and manage how the content is filtered. What does this mean for developers? This will give you the ability to set up a feed for say “Adobe AIR”, and use Filtrbox to manage the content. Managing the content allows you to choose how many articles are displayed, amount of history in days, and the quality ranking of the article. Once you have setup a feed, it then becomes as easy as consuming it. Most mashups consist of maps, graphing or basic interactions between multiple components that share relevancy with one another. With the ability to manage your own content sources, you become in control of your application and will not have to rely on restriction based upon the source. Dealing with restriction like the integration of Yahoo maps before the AS3 api, was troublesome and limited in its context.
When using the Filtrbox RSS, you have two options, either consume the RSS as is, or if you are passing the feed through flash embed variables, you will have to base 64 Decode the content. Using the feed in Flex is as easy as an HTTPService request. Here is an example of a Base 64 decoder to use:
import mx.utils.Base64Decoder;
var decoder:Base64Decoder = new Base64Decoder();
// this is decoding the embed variables that are passed in
decoder.decode(Application.application.parameters.source);
var byteArray:ByteArray = decoder.flush();
var src:String = byteArray.readUTFBytes(byteArray.length);
mw.source = 'http://www.filtrbox.com/rss/'+src;
So lately I have been interested in the Tour de France and wanted to create a quick app centered around that. The requirement of the app was to display the most up to date quality news about the Tour de France on my desktop as a widget. With the power of Filtrbox, I can add exclusions like steroids or other thing that i really dont want to deal with. If you just want to add the feed to your personal blog, you can embed code for the Filtrbox widget from the Filtrbox dashboard, that is built in Flex and is constantly developing and expanding into new semantic search territory.

Hello Flex, and welcome to Semantic Search
Today was the official launch of Filtrbox.com and its rich dashboard application. The idea behind Filtrbox is to provide a cure for information overload in a new and cost effective manner. Get this: “Built using Flex, the Filtrbox UI uses drag and drop, asynchronous and background updates, sophisticated graphing, user input validation, and dynamic screen layout to provide a smooth, low-drag user experience. This UI demonstrates that no matter how good your AJAX UI might be Flex will run rings around it.” -Mark Gibbs , Network World.
Now thats some sweet press. We really enjoyed building the UI and have really incorporated great features and components from Flex and the Flex community. It is great to have the new UI out and functional and highlighting the great features that Filtrbox has to offer. Stuff like Importing of Google analytics, the ability to search through archived news, seamlessly create, add, group filtrs( a Filtrbox search term), and analysis. Just to clarify, we do allow Google Alerts imports, and that is one of my favs. So now you can imports your alerts, and Filtrbox will consume them, in a easy to use interface that allow for sorting and filtering of archived data – hell ya.
We still have some awesome features up our sleeves, and you may possibly see a Filtrbox icon on your desktop (its and Adobe thing) soon. Many have wondered what presence Flex would play in Web 3.0 and what it means for a truly rich internet experience, but i hope at least some of these questions have been answered by Filtrbox and their commitment to an awesome user experience. This blog will become a place that highlights new features, thoughts, code examples, and over all news on what Filtrbox is doing and what it means to the Flex community.
Just a quick history if you dont want to read it at Filtrbox.com : Filtrbox was conceived by Ari Newman(The man with a plan) and Tom Chikoore( Mr. T – “I pity the fool”) and built by their main code monkey, Bruce Deen (Drinker of the Zima). I came on recently and redesigned the present UI that we launched with today. The new UI was completed in less than a month, thank you Mountain Dew for your support, and i hope that it can be considered a positive step forward for the integration of semantic search and RIA UIs. The application was conceived to bring about a new cost effective way to manage semantic news by giving you the ability to control your information and presentation. Being a small company, most of us have been awake for almost 48 hours, testing and deploying the application for you. I look forward to comments and feedback from user experiences and am just happy bring a new niche for Flex to the market.
Take some time out of your monday and check out Filtrbox.com
