Flash Player Security Settings Page Problems
Just in case anyone else is experiencing this. I noticed that the flash player security setting page was not working. Either the swf would not show up on the page, or it wouldn’t save settings you add to it.
I’m not entirely sure why this is happening, in terms of what’s going on with the domains / macromedia.com.
If you go here: http://macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html,you’ll see that either the swf doesn’t show, or it doesn’t save anything.
It’s missing the “www” in the URL, so go here: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html, and it will work.
2 commentsGity Is Open Source!
After much thought. I’ve decided to open source Gity. I’d really like to see it keep progressing and getting better. But I can’t do it myself. So if you like Gity or are interested in adding your own stuff. Do it!
Additionally, I’m moving my other git projects over to github.
3 commentsSimplifying iPhone UITableViewDataSource
I’ve been working the last couple weeks on an iPhone app. I’ve noticed that whenever I’m writing code that implements the UITableViewDataSource protocol it’s always the same code.
As I’ve been working I’ve been slowly coming back through all my code and writing re-usable objects and refactoring to use these new objects.
What I wanted was a (kind of) flex / flash like data provider object. This data provider implements the table view data source and has some nice convenience methods.
There’s three objects:
1. UITableViewDataProvider - the data source for a UITableView. It manages “groups” and contains a few accessor methods.
2. UITableViewGroup - the group manages a set of rows. And you can associate the section header titles and footer titles with the group.
3. UITableViewRow - a single row object contains a generic object for data. And it contains the code that creates and returns the “cell” for the table.
Here’s how you setup a data provider:
- (void) prepareTableData { tableData = [[UITableViewDataProvider alloc] init]; //table data is a member var UITableViewGroup * group1 = [UITableViewGroup group]; ACExploreResultRow * row1 = [ACExploreResultRow row]; ACSearchResultItem * item1 = [ACSearchResultItem itemWithTitle:@"Tag 3" andDesc:@"Desc" andThumb:[UIImage imageNamed:@"thumb.png"] andLarge:[UIImage imageNamed:@"large.png"]]; [item1 setRating:2.3]; [row1 setData:item1]; ACExploreResultRow * row2 = [ACExploreResultRow row]; ACSearchResultItem * item2 = [ACSearchResultItem itemWithTitle:@"Tag 4" andDesc:@"Desc" andThumb:[UIImage imageNamed:@"thumb.png"] andLarge:[UIImage imageNamed:@"large.png"]]; [item2 setRating:3.5]; [row2 setData:item2]; [group1 addRow:row1]; [group1 addRow:row2]; [tableData addGroup:group1]; [tableView setDataSource:tableData]; }
The only thing to point out is that the rows I’m creating are of type “ACExploreResultRow” which extends UITableViewRow. That row class is in charge of creating the cell to display.
Here’s the implementation for that class:
@implementation ACExploreResultRow + (ACExploreResultRow *) row { ACExploreResultRow * row = [[ACExploreResultRow alloc] init]; return [row autorelease]; } - (UITableViewCell *) cellForTable:(UITableView *) _tableView { UITableViewCell * cell = [super cellForTable:_tableView]; ACSearchResultItem * d = (ACSearchResultItem *)data; [cell setText:[d title]]; [cell setImage:[d thumb]]; return cell; } @end
This ends up being a really good place for the cell creation logic. In this class I’m using a straight up UITableViewCell, but the idea is that you could create your own subclassed UITableViewCell and use that instead. It’s also a good pattern for re-using your cells and rows or putting the cells in nibs.
Another benefit for doing it like this is that the data provider has methods for getting the data object associated with a row. As an example, your view controller will still implement the UITableViewDelegate protocol, and you’d use those hooks like normal.
- (void) tableView:(UITableView *) _tableView didSelectRowAtIndexPath:(NSIndexPath *) indexPath { ACSearchResultItem * item = (ACSearchResultItem *)[tableData dataForIndexPath:indexPath]; //yay we have data with one line of code! }
You can find the code and a sample project in gdkit.
No commentsRemoving Classes from MXMLC Link Reports
I started creating swcs for guttershark and ran into an annoying problem that I’m sure others have run into. In order to exclude classes from a swc you have to generate a link report from mxmlc. Then re-create the swc but tell mxmlc to load that report file and use it as the source for files to exclude.
That’s great and all, but the link report is usually huge, especially if you’re code links against flash components or flvplayback code. So in order to include only the classes you want (like only guttershark classes), I was starting to poke through the link report, and remove the guttershark classes.
Just to get this straight. MXMLC uses the link report as a source for actionscript classes to exclude from the swc. So if I want only the guttershark classes, you have to go into the link report and remove any guttershark class. The GS classes won’t be in the link report, which will cause them to be included in the swc.
Pretty simple; either poke through it and remove classes manually or try to write regular expressions to match only GS files. Then you think, “shit, this is a huge pain in the ass, and I’m going to waste a ton of time every time I need to update this link report.”
Here’s what I’m getting to: a python script to do it for you. This is the general usage sequence:
#...generate swc and link report with compc... python swc_link_report_remove.py -l report.xml -m "/Users/aaronsmith/Development/git/guttershark" -o gsreport.xml #...generate swc passing gsreport.xml for -load-externs to compc...
Here’s what the script does:
- Load the original report.xml and parse it
- Loop through each script item
- If the script name attribute matches against the -m property, skip the item.
- Write out the new link report file.
This is a much faster way to remove the classes you actually want included in the swc. I don’t use mxmlc or compc all that much, so maybe there is any easier way but the documentation certainly doesn’t outline any.
I’m on a mac, and you’ll need to install “lxml” for python.
sudo easy_install lxmlHere’s the script. Put it somewhere in your path for easy access and make sure it’s executable. Even if this isn’t exactly what you need, it’s a good starting point for scripting updates to the link reports.
No commentsGuttershark Updates
I finished the service layer updates. I ended up re-writing the flash remoting classes, and mostly re-writing the soap classes. There are a bunch more examples in the repository for working with http, remoting, and soap. It’s really good stuff and simplifies those layers even more than what’s in the AS3 api.
You may also notice one other update and it may or may not piss you off; I re-organized the package structure. All the examples have been updated. And I’ve been combing through the documentation again making updates. Mostly nit picky things. And I’ve been taking out examples from the documentation in favor of putting a note that states the examples are in the repository. It’s easier to maintain those examples, rather than hard coded examples in the source code.
2 commentsGuttershark Service Abstraction Updates
I’m working on revamping the service abstractions in Guttershark. I’ll be completely removing the ServiceManager in favor of using a class directly (like HTTPCall, SoapService, etc). I’ve got the http stuff done, and need to update soap and remoting. The http code is the most drastically changed. The soap and remoting code won’t change too much.
I’ve got a couple examples in the repo for the new HTTPCall class. But in short, here’s how you use it:
package { import gs.core.DocumentController; import gs.service.http.HTTPCall; import gs.service.http.HTTPCallResponseFormat; import gs.service.http.HTTPCallResult; public class Main extends DocumentController { private var hc:HTTPCall; public function Main() { super(); } override protected function setupComplete():void { hc=new HTTPCall("http://www.google.com/"); hc.responseFormat=HTTPCallResponseFormat.TEXT; hc.setCallbacks(onResult); hc.send(); } protected function onResult(r:HTTPCallResult):void { trace(r.text); } } }
And if you look through the docs, it supports a bunch of other stuff - retries, timeouts, more response formats than URLLoaderResponseFormat, and you can optionally set callbacks instead of using .addEventListener. Which is a nice alternative that allows you to setup the handlers you need in one line of code. Of course you can still opt in to using .addEventListener.
5 commentsCoda Plugin for Gity
Randomly came across this plugin for Coda which opens your project in Gity. Check it out.
No commentsNew TextAttributes Class in Guttershark
I added this new class called TextAttributes. It simplifies setting the right properties on a text field for text formats, stylesheets, a few other things, and the model has helpers to simplify it even more.
Here’s a model xml file:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="fonts" source="fonts.swf" preload="true" /> </assets> <fonts> <font libraryName="LucidaGrandeBold" /> <font libraryName="LucidaGrandeRegular" /> </fonts> <stylesheets> <stylesheet id="style1"> <![CDATA[ .body { color:#000000; font:LucidaGrandeBold; } .test { color:#ff0066; } ]]> </stylesheet> </stylesheets> <textAttributes> <attribute id="attr1" styleSheetId='style1' wrapInBodySpan="true" antiAliasType="advanced" /> </textAttributes> </model>
Simple enough, the model defines fonts, stylesheets, and now the additional textAttributes can be defined.
Here’s a guttershark document controller showing the use of text attributes.
package { import gs.core.DocumentController; import gs.core.Preloader; import flash.events.Event; import flash.text.TextField; public class Main extends DocumentController { public var t1:TextField; public var t2:TextField; override protected function flashvarsForStandalone():Object { return {model:"model.xml"}; } override protected function startPreload():void { preloader=new Preloader(); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.addItems(model.getAssetsForPreload()); preloader.start(); } override protected function onPreloadComplete(e:Event):void { super.onPreloadComplete(e); model.getTextAttributeById("attr1").apply(t1,t2); t1.htmlText = "<span class='body'>hello <span class='test'>world</span></span>"; } } }
The Model has a new method called getTextAttributeById which will give you an instance of a TextAttributes class, which coincidentally contains a method to apply it to any number of text fields.
The TextAttributes class can be used by itself, but having a helper method on the Model just simplifies defining all that information in XML.
There’s another example in the guttershark repo.
No commentsTracking With Guttershark
[UPDATE] This is all old code, there have been numerous updates in the GS repo. And there are examples in the repository. Use those instead.
I thought I’d take a minute to show how I implement tracking with guttershark. I’ve used this a lot and it always works. I wrote this tracking manager a while ago with the intentions of figuring out these problems:
- Provide some sort of assertions mechanism so the manager will first assert some condition, then fire or not fire.
- Fire different information based on if the assertion is true or false.
- Provide dynamic data in combination with tracking information from xml. Like the index of an image let’s say.
First thing’s first, here’s an example xml snippet:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
Currently the tracking manager only supports webtrends and hitbox. I haven’t written any projects that require anything else yet. There are hooks in the tracking manager for ganalytics, omniture, and atlas; so anyone else could implement it and share it with me to be put in GS.
The tracking manager will fire whatever tracking type you have defined in a track node. Here’s an example xml file with both webtrends and hitbox:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> <hitbox> <lpos>asdf</lpos> <lid>ghjk</lid> </hitbox> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
So the tracking node “tag1″ would fire both webtrends and hitbox. This also shows that depending on the tracking type, the tracking manager will parse the xml differently.
Let’s take the webtrends tag for example. The tracking manager will split the content on a comma (”,”), and use index 0 as “dcsuri”, index 1 as “ti”, and index 2 as “cg_n”. Those are webtrends variables passed to their dcsMultiTrack function.
So let’s take a look at getting this to fire. Here’s a document class that would do it:
package { import gs.core.DocumentController; import gs.core.Preloader; import gs.util.MathUtils; import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class Main extends DocumentController { public var clip:MovieClip; override protected function flashvarsForStandalone():Object { //the model file contains the tracking.xml file asset return {model:"model.xml"}; } override protected function onModelReady():void { preloader=new Preloader(); //preloader will load the tracking.xml asset preloader.addItems(model.getAssetsForPreload()); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.start(); } override protected function initTracking():void { super.initTracking(); //tracking is initialized in DocumentController //tell the tracking manager to fire the "tag1" track when clip is clicked. tracking.register(clip,MouseEvent.CLICK,"tag1"); } } }
Because this is using the guttershark framework there’s one hidden thing. Guttershark will load a model.xml file which contains our tracking.xml file asset.
Here’s what that model.xml file looks like:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="tracking" src="tracking.xml" preload="true" /> </assets> </model>
All the model xml does is define a tracking asset which is an xml file. The guttershark document controller will find that asset, and initialize a tracking manager for us. It’s actually really straightforward. All the DocumentController does is expose hooks and sets up some default things that I use all the time. Read the source for DocumentController for more info. Anyway, back to the tracking.
With everything that we’ve setup so far. A tracking call would be fired for “tag1″ when you click on the movie clip. This is where it starts to get interesting.
Let’s say we didn’t want the tracking to fire if clip is disabled. Here’s how you can do that:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"enabled" }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } ...
The tracking manager internally will end up using “if(clip["enabled"]) fire()”. The tracking manager can also use a method:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertMethod:shouldFire }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function shouldFire():Boolean { return (clip.alpha > .25) ? true : false; } ...
With that update, the tracking event would only fire if the clip’s alpha is greater than .25.
That covers some of the basic assertions.
What if we wanted to choose which tag to fire depending on if the assertion is true or false? First let’s make an update to the xml:
<?xml version="1.0" encoding="utf-8"?> <tracking> ... <track id="clipWasShown"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="clipWasHidden"> <webtrends>tag1,tag2,tag3</webtrends> </track> ... </tracking>
And here’s how I’d update the actionscript to accommodate this.
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"visible", whenTrue:"clipWasShown", whenFalse:"clipWasHidden" }; tracking.register(clip,MouseEvent.CLICK,null,options); } ...
That’s really just another useful option in the tracking manager arsenal.
Now for the last problem, dynamic data. Let’s say we wanted to include the index of some slideshow, which indicates which picture their looking at. And let’s say we want it appended to the “cg_n” variable that get’s sent to webtrends.
Here’s how you can implement dynamic data:
... private var index:int = 0; private function next():void { index++; render(); } private function prev():void { index--; render(); } override protected function initTracking():void { super.initTracking(); var options:Object = { dynamicData:slideShowIndex }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function slideShowIndex():Array { return [null,null,index]; } ...
So in this case, the tracking manager expects an array for the “dynamic data” for webtrends. And will append whatever is in the array to the tags from xml before firing.
The dynamic data just happens to be implemented this way for webtrends. But if it’s a different tracking type you would return whatever the tracking manager is expecting. So for example, the “hitbox” tracking would require a function like this:
private function slideShowIndex():Object { return {lpos:index}; }
That’s pretty much it. As mentioned, I don’t have omniture, ganalytics, or atlas implemented, but there are hooks in the tracking manager. So if anyone out there needs to implement then please share.
I like using the tracking manager this way because it limits the amount of code I have to write to actually fire tracking. And keeps the real tracking tags out of your code.
There’s one other thing to mention - whenever you’re working with someone from an analytics department who is creating the tags for you. Make sure they know to always put dynamic data at the end of the tags. It’s easier to implement ;)
There are a couple examples in the guttershark repository.
1 commentNew KeyHandler Class
I’ve added a new class to replace the KeyManager. The key manager was nice but somewhat complicated internally. I’m replacing it with this new key handler class - It’s similar to the key manager except that it will only handle one key event per instance VS the key manager which would manage every key event you wanted.
I added a new feature called auto target, which will enable, or disable the key handler when the target is removed or added to the stage.
Here’s how you use it:
package { import gs.util.KeyHandler; import gs.util.StageRef; import flash.display.MovieClip; import flash.display.Sprite; import flash.utils.setTimeout; public class Main extends Sprite { public var mc:MovieClip; private var kh:KeyHandler; private var kh2:KeyHandler; private var kh3:KeyHandler; private var kh4:KeyHandler; public function Main():void { //the stage ref needs to be set somewhere StageRef.stage=stage; //create key handlers. kh=new KeyHandler("SHIFT+M",onShiftM); kh2=new KeyHandler("UP",onUp); kh3=new KeyHandler("f",onLetter); kh4=new KeyHandler("as",onSequence); //auto target example (enabled/disabled depending on if the target is on the stage) //the "kh" keyHandler won't work when the clip is off the stage. kh.autoTarget=mc; setTimeout(removeChild,2000,mc); setTimeout(addChild,4000,mc); } private function onSequence():void { trace("sequence as"); } private function onLetter():void { trace("letter f"); } private function onShiftM():void { trace("SHIFT+M"); } private function onUp():void { trace("UP"); } } }
There’s a functioning example in the guttershark repo.
2 comments