Implementing a simple Supervising Presenter pattern with session persistence for Mobile Flex 4.5 (Hero) SDK
Looking at different flavours of the Presenter pattern for Flex I came across Supervising Presenter, which sounded like a nicely balanced way of separating View and Logic, but without getting too extreme like Passive View and throwing away the nice declarativeness of MXML.
What I also wanted to achieve is to completely get rid of any ActionScript from MXML as I find it really messy, but not using the simple fx:Script include as that only seemingly solves the problem by tucking away the code, but keeping it completely out of the package and inheritance structure. I considered using the Flex code behind pattern too, but for me the direction of inheritance made no sense as I'd want to extend an MXML UI using code, not the other way round – mind you, it makes sense for skinning components, but not for implementing a Presenter pattern. The all around best solution I found was Avi Tzurel's, who injects the Presenter (or as he calls Helper) using the fx:Declarations way.
In order to test my idea, I set out to create a Supervising Presenter which implements a simple button click handler and serialises its data class against which I can bind UI controls and maintain their states within a session while changing Views in my mobile application.
The inspiration for this came from Todd Anderson's similar experiment which was a great start for me to understand persistence with Flex mobile and with his explanation the idea behind the pattern itself, but apart from the fact that his implementation didn't work with the final Flex 4.5 SDK, I also don't like that both the MXML and the Presenter have code in them and there are tons of boilerplate code in both.
So I started with an ideal MXML implementation (OK, to be honest ended up with after a lot of trial and error), which has no code, but has all the bindings and interaction event handlers declared:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:presenters="me.danieldemmel.example.views.presenters.*"
creationComplete="presenter.init()"
title="home">
<fx:Declarations>
<presenters:HomeViewPresenter view="{this}" id="presenter" />
</fx:Declarations>
<s:navigationContent />
<s:layout>
<s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10" />
</s:layout>
<s:TextInput text="@{presenter.data.UserName}" width="100%" />
<s:TextInput text="@{presenter.data.Password}" width="100%" displayAsPassword="true" />
<s:HSlider value="@{presenter.data.SliderValue}" width="100%" />
<s:Button label="Go to next view" click="presenter.handleLogInButtonClick(event)" />
</s:View>
Then I created SupervisingPresenterBase class to handle some of the View persistence boilerplate:
package me.danieldemmel.example.core
{
import flash.events.EventDispatcher;
import spark.events.ViewNavigatorEvent;
import spark.components.View;
public class SupervisingPresenterBase extends EventDispatcher
{
private var _view:View;
public function SupervisingPresenterBase() {}
public function init():void
{
_view.addEventListener( ViewNavigatorEvent.VIEW_DEACTIVATE, handleDeactivate, false, 0, true );
if(_view.data == null)
_view.data = {};
}
// Is called before navigator.destroy of this instance. Serialize the data property and clear out event handler for GC.
protected function handleDeactivate( evt:ViewNavigatorEvent ):void
{
_view.removeEventListener( ViewNavigatorEvent.VIEW_DEACTIVATE, handleDeactivate, false );
serializeSessionData();
}
protected function serializeSessionData():void
{
// override this method to serialise typed data on view destroy
}
public function set view(v:View):void
{
_view = v;
}
public function get view():View
{
return _view;
}
}
}
And I also created a typed data class to bind against, so that I have something light to serialise:
package me.danieldemmel.example.views.presenters
{
public class HomeViewPresenterData
{
public function HomeViewPresenterData() {}
public var UserName:String;
public var Password:String;
public var SliderValue:Number;
}
}
To be able to do only the button event handling and the type serialising in the actual Presenter:
package me.danieldemmel.example.views.presenters
{
import flash.events.MouseEvent;
import me.danieldemmel.example.core.SupervisingPresenterBase;
import me.danieldemmel.example.views.HomeView;
import me.danieldemmel.example.views.WelcomeView;
[Bindable]
public class HomeViewPresenter extends SupervisingPresenterBase
{
public var data:HomeViewPresenterData;
public function HomeViewPresenter()
{
super();
}
public function get myView():HomeView
{
return HomeView(this.view);
}
public override function init():void
{
super.init();
// try to deserialise session data
if(myView.data["presenterData"] == null) data = new HomeViewPresenterData();
else data = HomeViewPresenterData( myView.data["presenterData"] );
}
protected override function serializeSessionData():void
{
myView.data["presenterData"] = data;
}
public function handleLogInButtonClick( evt:MouseEvent ):void
{
myView.navigator.pushView( WelcomeView );
}
}
}
Finally here's the project file if you want to see all the details: http://dl.dropbox.com/u/100064/MobileTestBinding.fxp
Hope I managed to help someone with this, I think I'm going to explore this patter further for my application as I'm wary of picking up any desktop Flex framework for mobile development.
Presentation Model and Multiple Screens - fixing Brian Genisio's ScoreKeeper sample to work with final Flex Mobile 4.5 (Hero) SDK
If you were looking for a good code example for Presentation Model (PM or MVPM) pattern for Flex, you probably ran into the brilliant post by Brian Genisio and Charlie Sears on riarockstars.com: http://riarockstars.com/2011/03/16/presentation-model-and-multiple-screens-part-1/
Unfortunately as pretty much all examples using the prerelease / beta Flex Hero SDK it won't work as is, due to some renamed classes.
Well fear not, I managed to fix it, only had to do a few small replacements. It doesn't look the same for some reason, but it compiles and works perfectly, including persistence, so perfect starting point to tinker with!
Grab the FXP here: http://dl.dropbox.com/u/100064/ScoreKeeper.fxp
Reskinning the Android contextual menu (ViewMenu and ViewMenuItems) in Flex / AIR mobile to look like Gingerbread black
In this example we're going to tweak the built in ViewMenu and ViewMenuItem skins coming with Flex Mobile, which were designed with Froyo in mind. Google in the meanwhile however decided to switch the lights off and changed most of the theme to black.
Gingerbread context menu reference screenshots (well it's Cyanogenmod actually)
For busy people or if you prefer to follow along more closely, here's the FXP with the final outcome:
http://dl.dropbox.com/u/100064/ViewMenuTest.fxp
Adding some ViewMenuItems
Create a home view and throw in some items to test with:
<s:viewMenuItems> <s:ViewMenuItem label="Copy" click="doNothing(event)"/> <s:ViewMenuItem label="Cut" click="doNothing(event)"/> <s:ViewMenuItem label="Paste" click="doNothing(event)"/> <s:ViewMenuItem label="Undo" click="doNothing(event)"/> <s:ViewMenuItem label="Options" click="doNothing(event)"/> </s:viewMenuItems>
Opening the ViewMenu programatically
First things first, in order to be able test in the emulator easily (or trigger it yourself for any reason) here's how you can toggle the ViewMenu from a View:
<s:Button label="Open ViewMenu" click="openViewMenu(event)"/>
protected function openViewMenu(event:MouseEvent):void
{
this.parentApplication.viewMenuOpen = true;
}
Otherwise you can open it with ⌘N in OS X (not sure about Windows).
Finding, copying and using the built in skin files
Making use of Adobe's generous open sourcing of the Flex SDK, let's hunt down the skin files we need to modify for this example, here's where they are in my case:
/Applications/Adobe Flash Builder 4.5/sdks/4.5.0/frameworks/projects/mobiletheme/src/spark/skins/mobile/ViewMenuItemSkin.as
/Applications/Adobe Flash Builder 4.5/sdks/4.5.0/frameworks/projects/mobiletheme/src/spark/skins/mobile/ViewMenuSkin.mxml
Copy them into `skins` folder in your own project and rename them to CustomViewMenuItemSkin.as and CustomViewMenuSkin.mxml respectively, updating the packages inside too.
We also need to grab the FXG files for the ViewMenuItem, for those you'll need to copy 2 sets for the different device DPIs. Copy these 3 files:
ViewMenuItem_down.fxg
ViewMenuItem_showsCaret.fxg
ViewMenuItem_up.fxg
...from these 2 folders:
/Applications/Adobe Flash Builder 4.5/sdks/4.5.0/frameworks/projects/mobiletheme/src/spark/skins/mobile/assets
/Applications/Adobe Flash Builder 4.5/sdks/4.5.0/frameworks/projects/mobiletheme/src/spark/skins/mobile320/assets
...to `assets` and `assets320` folders in your project respectively.
You'll need to go through the references in the CustomViewMenuItemSkin.as constructor and update the references of the component states to these local FXGs:
upBorderSkin = assets320.ViewMenuItem_up; downBorderSkin = assets320.ViewMenuItem_down; showsCaretBorderSkin = assets320.ViewMenuItem_showsCaret;
and
upBorderSkin = assets.ViewMenuItem_up; downBorderSkin = assets.ViewMenuItem_down; showsCaretBorderSkin = assets.ViewMenuItem_showsCaret;
Applying the custom skins in CSS and setting the text to white
Create a CSS file and set up the custom skins:
s|ViewMenuItem
{
color:#ffffff;
skinClass: ClassReference("skins.CustomViewMenuItemSkin");
}
s|ViewMenu
{
skinClass: ClassReference("skins.CustomViewMenuSkin");
}
Apply the CSS in the main application MXML:
<fx:Style source="css/global.css"/>
Modifying the MXML based ViewMenu skin
This is pretty much standard Flex 4 style Spark theming, but what we can do here is to remove the bottom stroke to make it look more like the Android native menu, in:
<s:Group id="contentGroup" left="0" right="0" top="3" bottom="2" minWidth="0" minHeight="0">
change `bottom` to 0 and in `updateDisplayList` override delete the line:
contentGroup.bottom = separatorWeight;
As far as I could test, you can also safely get rid of the background completely, we can set up the right colour purely in the ViewMenuItems later:
<!-- Background --> <s:Rect left="0" right="0" top="1" bottom="0" id="background"> <s:fill> <s:SolidColor color="#000000" alpha=".5"/> </s:fill> </s:Rect>
Modifying ActionScript and FXG based ViewMenuItem skins
First let's tweak the FXGs and try to put as much graphics in them as possible to make use of the fact that they are precompiled! In `ViewMenuItem_up.fxg` let's set up the only very slightly transparent black background:
<SolidColor color="#000000" alpha=".9"/>
In `ViewMenuItem_down.fxg` we can remove the transparent rectangle, it's not needed as the `overlay` is the same size:
<!-- Transparent Rect to ensure proper scaling --> <Rect width="158" height="100" x="0" y="0"> <fill> <SolidColor color="#000000" alpha="0"/> </fill> </Rect>
change the `overlay` gradient colours to the Gingerbread orange:
<LinearGradient rotation = "90"> <GradientEntry color="#ff9000" ratio="0" alpha="1"/> <GradientEntry color="#ff6600" ratio="1" alpha="1"/> </LinearGradient>
Lastly, in `ViewMenuItem_showsCaret.fxg` replace the transparent rectangle with the slightly darker orange:
<!-- overlay --> <Rect width="159" height="100" x="0" y="0"> <fill> <LinearGradient rotation = "90"> <GradientEntry color="#ffc700" ratio="0" alpha="1"/> <GradientEntry color="#ffaa00" ratio="1" alpha="1"/> </LinearGradient> </fill> </Rect>
Now in `CustomViewMenuItemSkin` we can remove the dynamically rendered background since we are taking care of it in the FXG files:
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
// omit background rendering in code and use FXG for background instead
}
Also, let's hide text shadow as the Android context menu is not doing any of that and it's a performance sink having to render complex font vectors anyway:
override protected function createChildren():void
{
super.createChildren();
// hide the labelDisplayShadow text
labelDisplayShadow.visible = false;
}
The best would be to remove it entirely, but `ButtonSkin` which it extends is referencing it. You can customise it as an exercise if you feel pedantic :)
Now to make the text visible for `down` and `showsCaret` states, change the colour on state changes instead:
override public function set currentState(value:String):void
{
super.currentState = value;
switch(value)
{
case "up":
labelDisplay.textColor = 0xffffff;
break;
case "down":
case "showsCaret":
labelDisplay.textColor = 0x000000;
break;
}
}
If you're still reading, pat yourself on the back, it's been an epic post and a lot to take in :)
As a little bonus I've added some icons to the ViewMenuItems to make them look nicer, take a look if you haven't downloaded the FXP yet!
Transitioning between Views in a Mobile Flex Application (AIR for Android) with swipe gestures
This post is an update on Using Swipe Gestures in Mobile Flex Application (AIR for Android) from Flex-Blog, and particularly making the full code example from Tour de Flex work with the final Flex 4.5 SDK (and in my case AIR 2.7) libraries.
If you're not interested in the actual changes and just want the working stuff, download the FXP which builds fine and runs on my HTC Desire handset: http://dl.dropbox.com/u/100064/AndroidTest.fxp
So first of all what you have to change is the root node in the main MXML, from MobileApplication to ViewNavigatorApplication to make it work. Similarly, TabbedMobileApplication is now TabbedViewNavigatorApplication in case you run into that elsewhere.
Transitions between the views also work somewhat differently, with direction constants now coming from a dedicated static class called ViewTransitionDirection, SlideViewTransition now finding its new home in spark.transitions package and not taking parameters via the constructor anymore. The navigator.pushView method takes one more argument too, had to pad in an extra null compared to the previous example, so in the home view you'll need to use something more like this:
<fx:Script>
<![CDATA[
import spark.transitions.SlideViewTransition;
import spark.transitions.ViewTransitionDirection;
private function handleSwipe(event:TransformGestureEvent):void
{
var slideViewTransition:SlideViewTransition = new SlideViewTransition();
// Swipe was to the right
if (event.offsetX == 1 ) {
slideViewTransition.direction = ViewTransitionDirection.RIGHT;
navigator.pushView( PreviousView, null, null, slideViewTransition );
}
// Swipe was to the left
else if (event.offsetX == -1 ) {
slideViewTransition.direction = ViewTransitionDirection.LEFT;
navigator.pushView( NextView, null, null, slideViewTransition );
}
}
]]>
</fx:Script>
I think that's it, feel free to leave a comment!
How to import sample data source from one Expression Blend 4 solution to another
Had to spend some time to work this out, apparently Expression Blend only gives you the option to import sample data from XML, but I couldn`t find a way to export.
There`s an easy workaround however, just create a new sample data source in the target solution and once that`s done, overwrite the XAML + CS + XSD files from the source solution. Now Blend will ask you to reload files changed outside of it, just press yes and you`re all set!
Energy efficient and cheap SMB based NAS for OS X Time Machine backups using Fonera router
In this post I’ll try write up my experience with the specific devices I have, in my epic struggle to make Time Machine work wirelessly.
Ingredients
A USB NAS enabled router
If you don’t yet have a fast (by that I mean at least 802.11n, not older ‘g’ or ‘b’) Wi-Fi router with a USB port for your networked storage needs I recommend getting a Fonera 2.0N:
Since the last firmware update solved some disconnection problems it’s a very nice device with the added bonus of joining a 2 million strong Wi-Fi community with mutually shared hotspots around the globe. The price is €79 / $99 at the time of writing (+€10 shipping).
By being just a small embedded Linux router (based on OpenWrt) it hardly uses any power, but the hardware inside is still good enough to handle up to 5-10MBytes / sec transfer speeds over local network.
The Fonera will create a Samba (SMB) based share, so any devices using that should be similar to set up.
An external USB hard disk
I got myself one of the Western Digital Elements drives:
It’s cheap, no fluff, small, silent and energy efficient by spinning down when not in use for some time.
Note: because of the spinning down functionality Time Machine will fail with the regular backup if your Mac is not backing up for a few hours. That’s because Time Machine is too impatient to wait for the drive to start up. When this happens just open Finder, click on your server (e.g. LaFonera) and the backup volume (e.g. Backups) to mount it and then start a manual backup.
A Mac with Snow Leopard installed
I’m using 10.6.3 at the time of writing, so not sure if the process is same for other versions of OS X.
Setting things up
Formatting the drive
First, plug in your USB hard disk and format it to HFS+, but without journaling. This is an older Mac file system which works well with both Fonera and your Mac (if you will want to plug in directly later for a full restore for example).
You can do this with Disk Utility by clicking on your external hard drive on the left panel, and then selecting Mac OX Extended from the drop down menu and clicking Erase… button in the bottom right corner:

This shouldn’t take more than half minute if everything goes well, even with terabyte sized drives.
Setting up NAS functionality
Now it’s time to eject the drive and plug it into Fonera.
Once the drive powered up open your browser and type in fonera to open the Fonera interface. First make sure that you’re running at least 2.3.6.0 version of the firmware. If you do, click on USB Disc.

Once in there, your hard disk should show up, ready to be set up (I actually have 2 of them and use the older, 500GB one for backups).

Choose Folder as mode from the drop-down and give a name to your hard disk, mine is called Backups.
If you plan to use the same disk for other Fonera functions like torrent or uploads, make sure you set Create folders to enabled on the bottom.
Once that’s done you have to make sure that the network shares are set up properly, go back to the Fonera dashboard and click Settings. Once in there click Fileserver.

Here you have to make sure that Windows Network Shares are enabled, and optionally you can also turn on FTP access on the top if you want.

If everything is set up right you should be able to browse your hard disk if you go to Filebrowser in Fonera dashboard.
There’s a little delay before the network share gets activated and your Mac discovers it, so wait about half minute and then open Finder and try to connect to your Fonera. It won’t allow guest connections, so put in fonero as username and your Fonera admin password to access the shares. Make sure you save the password to Keychain, Time Machine will need it to access the network drive on its own later.

Setting up a Sparsebundle to store your backups
Now you have to create a disk image to store your backups. It is needed so that your files will be neatly tucked into one big image file and more importantly because Time Machine needs the full featured HFS+J file system (same as your Mac).
In order to be able to do this you’ll need 2 things first, the name of your Mac and your Ethernet MAC address.
To find out your Mac’s name, click the Apple logo in the menu bar and open System Preferences, then open Sharing.

Here you can see the network name of your computer. I recommend making it a safe one for compatibility reasons, so only letters, numbers, underscores and dashes.

Note down you Mac’s name or leave that window open for now. Then open up Terminal and copy / paste the command below.
ifconfig en0 | grep ether
You should get a line of response like below, copy the number part and remove the colons.
ether 00:a6:4a:33:bb:11
Now you’re ready to create your sparsebundle! Copy / paste the command below into Terminal, edit size to be equal or bigger than your Mac’s hard disk size and edit the sparsebundle to be your Mac’s name, an underscore and your Ethernet MAC address without the colons.
hdiutil create -size 500G -fs HFS+J -volname 'Time Machine Backups' -type SPARSEBUNDLE yourmacsname_00a64a33bb11.sparsebundle
This will create the image in your home folder and shouldn’t take more than a few seconds. Once Terminal lets you know that it’s done, open up your home folder in Finder and double click the sparseimage to mount it.
Now download the sample com.apple.TimeMachine.MachineID.plist, this is a tiny XML file you’ll have to edit and put inside your mounted disk image.
Click on the Apple logo in the menu and open About This Mac. Click More Info… and copy your Hardware UUID.

You’ll have to paste this into the plist file you just downloaded, replacing UUID COMES HERE. Save it and copy into the root folder of your disk image. After it copied eject the image.
Now open up the Fonera network share in Finder and drag your sparsebundle from your home folder to the root folder of Backups (or whatever name you gave your hard disk share on Fonera)
Setting up Time Machine backups
While the file is copying you have to make Time Machine accept Fonera share as a valid place to store your backups. Apple uses AFP shares on Time Capsule, so using SMB is not ideal, but it should work fine anyway.
So open up Terminal and copy / paste the command below.
defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1
When copying the sparsebundle is finished, you can open up Time Machine from System Preferences, click Select Disk…, choose your Fonera share and click Use for Backup.

Now the backup should start in 2 minutes automatically, or you can start it immediately by clicking the little Time Machine icon in your menu bar and Backup Now.
It’ll first verify your backup image and if you set up everything right it should start backing up. This initial backup can take more than a full day if you do it over Wi-Fi, so make sure that your Mac can be turned on and connected for that long, you can’t pause and resume the backup process.
Caveat #1
To make things a little bit more complicated, an OS X patch came up via Software Update about a week after I had everything set up, called Time Capsule Backup Update 1.0. This only comes up for people with network Time Machine set up and as an extra touch from Apple you can’t directly download it. This update unfortunately forced me to resave the backup, so at the moment I’m waiting for 160GBytes to be stored from scratch again :( So I highly recommend running Software Update (under Apple logo in menu bar) as soon as you have things up and running.
Caveat #2
Seems that the Apple made Time Machine ruthless with faulty backups, it happened to me twice now that due to a single failed backup it wiped out the entire sparsebundle :( So for the time being I switched back to backup via USB as I’m using my laptop for work and I can’t risk not being protected.
Sources:
Learn creativity from Dr. NakaMats, world recorder genius with 3300+ patents: "Good smell is good camera"!
Just seen an interview with Dr. Yoshiro Nakamatsu, charming guy, he`s talking about his genius as if it was the most natural thing on Earth. I can kind of understand him though, having done all what he managed throughout his life it would make no sense to be too modest :)
Chic:So, like Edison, you're awake most of the time. Do you agree with Edison's claim that ideas are 1 percent inspiration and 99 percent perspiration?Nakamatsu:No, now it's just the opposite! Now it's 1 percent perspiration and 99 percent "ikispiration." Now, more than ever, we have to have ikispiration. This means I encourage myself to go through my three elements of creation: suji, the theory of knowledge; pika, inspiration; and iki, practicality, feasibility, and marketability. In order to be successful, you must go through all three stages and make sure that your ideas stand up to all of them, which is ikispiration. Also, these days, the computer saves time and cuts out the 99 percent perspiration.Chic:Do you find that most American research-and-development firms take themselves through your three stages?Nakamatsu:Most are very thorough with suji, or theory, but don't concentrate on the iki, marketability. Hardest of all, of course, is pika, the creative inspiration. Researchers often have trouble with pika because they're too focused on one particular element. A genius must be a well-rounded person, familiar with many things: art, music, science, sports. He or she can't be restricted to only one field of expertise.
Nakamatsu:...for every meeting, I like to keep a visual record. That's why my wife has been taking pictures and recording our conversation on the camcorder. When something is on video, I can go back and reference the face and the voice, not just written notes. Now, would you please type in your name?Chic:Excuse me?Nakamatsu:Type in your name on this infrared recorder, and it will appear directly on the photographs that we took, along with today's date.Chic:I've never seen anything like this!Nakamatsu:I know. One of my recent inventions.
Mobile Trends 2020
Why did I start a Posterous feed?
Always felt the need for a good place to put medium-form posts, but Tumblr never quite managed to entice me with what they do. While they aggregate other streams nicely, what I needed is something which can push what I post to the right social networks which is exactly what Posterous does. Also, the simple but clever posting via email is brilliant, such an original way to create a platform-independent interface. So here I am, let`s see how it goes :)
Just for the reference, this is the article which nicely and clearly pointed out these differences for me:
http://lifestreamblog.com/my-thoughts-on-posterous-as-a-lifestreaming-platform/





3 Comments