Support for screen navigation and application session caching for QNX PlayBook apps

If you’ve ever worked with Flex “Hero” mobile components you probably love the way Flex “Hero” handles the screen metaphor. It provides a neat way to encapsulate the UI and business logic for each screen in a class.

On a mobile device, you need to keep in mind that an application can be closed anytime by the operating system. This could be because the OS is trying to free some resources or maybe the battery just died. In either case when the user reopens the app, he expects to see the last screen he was working on as well as any data that was entered. Again, with Flex “Hero” mobile applications all you have to do is to set the sessionCachingEnabled to true and assign the information you want to be persisted to the data property of your views.

This is great; however if you develop applications for PlayBook using the QNX libraries (the native Tablet OS SDK for Adobe AIR) you can’t use Flex “Hero” and, as a result, you don’t have support for the screen metaphor. I wanted this kind of support for an application I plan to build for PlayBook.

Lucky for me, I didn’t have to start from scratch because my fellow evangelist, Piotr Walczyszyn, has already created a small library that does nice tweening between two screens and implements the methods for adding a screen or popping one. So, I took his library and I modified it a little bit. Why did I modify the library? First, I wanted to have session caching capabilities and to be able to push data from one screen to another in the same manner as a Flex “Hero” mobile application. And second, I wanted to make it easier to work with QNX UI components (the original library supported generic ActionScript mobile projects).

Basic usage

To use this library you have to add the library code to your project and create classes that extends org.corlan.asviews.BaseView for each view/screen you need for your application.

In the main application file you create an instance of the org.corlan.asviews.ViewNavigator class. You use its pushView() method to define the first view/screen and to move to new screens. You use the popView() method to move back to the previous screen/view or popToFirstView() to jump directly to the first view/screen.

If you need session caching for your app there are two things you have to do. First, when you create the ViewNavigator instance, you set the second argument to true. Second, you register the ViewNavigator.prepareForClosing() method as an event listener for the application Close event:

 NativeApplication.nativeApplication.addEventListener(
     Event.EXITING, navigator.prepareForClosing, false, 0, true);

See this screencast for more information or read on the next section.

Downloading and using the library

You can download the sample project from here and the library from here.

It is quite simple to use it once you set up the library project and add it as a dependency to your ActionScript mobile project. Here are the steps:

  1. Download the as3viewnavigator library from here and import it in Flash Builder Burrito. Then open the project properties dialig box, select Flex Library Build Path, and make sure that qnx-screen.swc file is added to the build path (you can find this swc in the sdks/blackberry-tablet-sdk-0.9.2/frameworks/libs/qnx-screen/ folder inside the Flash Builder Burrito installation folder).
  2. Create a new ActionScript Mobile Project for PlayBook development. Then open the project properties dialog box, select ActionScript Build Path, click the Add Project, and choose the as3viewnavigator project.

Now you are ready to use it. The basic steps are:

  • For each screen you want, create a class that extends the org.corlan.asviews.BaseView (this class extends the QNX Container class and implements the org.corlan.asviews.IView interface). When you create the screen classes you have to keep in mind that you need to override two methods from the BaseView class:
    • createUI() – this is where you create the UI for the screen using the components from the QNX libraries or your own components.
    • applyData() – you use this method to populate the UI with the data saved in the previous session. This method is called automatically once your screen class is added to the stage.
  • Store whatever data you want to be persisted in the data property of your screen classes
  • In the main application file, you instantiate the org.corlan.asviews.ViewNavigator class and push the first screen. Also, you need to register the ViewNavigator.prepareForClosing method to listen for application Close events.
  • In any screen that you need to push a new screen (or move back) you just call the navigator.pushView(View-Class-Name, data), navigator.popView(), or navigator.popToFirstView() methods

Now let’s see some snippets of code to better understand the workflow. First here is the code for the main application file:

[SWF(height="600", width="1024", frameRate="30", backgroundColor="#FFFFFF")]
public class ASViews extends Sprite
{

	private var navigator:ViewNavigator;

	public function ASViews()
	{
		stage.align = StageAlign.TOP_LEFT;
		stage.scaleMode = StageScaleMode.NO_SCALE;

		// Creating an instance of ViewNavigator
		// if second argument is set to true then the history
		// is persisted between sessions
		navigator = new ViewNavigator(this, true);
		// Pushing first view to the navigator
		navigator.pushView(HomeScreen);
		//listening for application closing event
		//in order to save the history
		NativeApplication.nativeApplication.addEventListener(Event.EXITING,
				navigator.prepareForClosing, false, 0, true)
	}

}

And here is the code for the first screen, HomeScreen (in the onItemSelected() method I push the second screen passing along the current selected item from the list to be used in the second screen):

public class HomeScreen extends BaseView
{

	private var list:List;

	public function HomeScreen(s:Number=100, su:String="percent")
	{
		super(s, su);
	}

	override protected function createUI():void {
		flow = ContainerFlow.HORIZONTAL;

		var optionsArr:Array = [
			{label: "Flash Gordon - The fastest man on earth!"},
			{label: "Superman - This guy could use some fashion advisory!"},
			{label: "Batman - The coolest bat in town!"},
			{label: "Green Lantern - Not a friend of Green Hornet"},
			{label: "Green Hornet - Can be tricked with some good coffee!"}
		];

		//list with the options
		list = new List();
		list.containment = Containment.BACKGROUND;
		list.dataProvider = new DataProvider(optionsArr);
		list.selectionMode = ListSelectionMode.SINGLE;
		list.addEventListener(ListEvent.ITEM_CLICKED, 
                        onItemSelected, false, 0, true);

		addChild(list);
	}

	private function onItemSelected(e:ListEvent):void {
		if (!data)
			data = new Object();
		data["selectedItem"] = list.selectedIndex;
		navigator.pushView(SecondScreen, list.selectedItem);
	}

	override protected function applyData():void {
		trace("applyData() HomeScreen");
		if (!data)
			return;
		if (data["selectedItem"])
			list.selectedIndex = data["selectedItem"];
	}
}

This is the code for the second screen, SecondScreen (in the onGoBack() method I call the navigator.popView() method to move back to the previous screen):

public class SecondScreen extends BaseView
{

private var lbl:Label;

public function SecondScreen(s:Number=100, su:String="percent")
{
	super(s, su);
}

override protected function createUI():void
{
	flow = ContainerFlow.VERTICAL;

	var topContainer:Container = new Container();
	//			topContainer.margins = Vector.([1,1,1,1]);
	//			topContainer.debugColor = 0x33FF33;
	topContainer.size = 50;
	topContainer.sizeUnit = SizeUnit.PIXELS;
	topContainer.flow = ContainerFlow.HORIZONTAL;
	topContainer.containment = Containment.DOCK_TOP;

	var left:Container = new Container();
	//			left.margins = Vector.([1,1,1,1]);
	//			left.debugColor = 0x33FF33;
	left.size = 70;
	left.sizeUnit = SizeUnit.PERCENT;
	left.flow = ContainerFlow.VERTICAL;
	left.align = ContainerAlign.NEAR;

	var right:Container = new Container();
	//			right.margins = Vector.([1,1,1,1]);
	//			right.debugColor = 0x33FF33;
	right.size = 30;
	right.sizeUnit = SizeUnit.PERCENT;
	right.flow = ContainerFlow.VERTICAL;
	right.align = ContainerAlign.FAR;

	lbl = new Label();
	lbl.text = "";
	lbl.autoSize = TextFieldAutoSize.CENTER;

	var backBtn:BackButton = new BackButton();
	backBtn.label = "Go Back";
	backBtn.addEventListener(MouseEvent.CLICK, onGoBack, false, 0, true);

	left.addChild(new Spacer(50));
	left.addChild(lbl);
	right.addChild(new Spacer(1));
	right.addChild(backBtn);

	topContainer.addChild(left);
	topContainer.addChild(right);
	addChild(topContainer);
}

private function onGoBack(e:MouseEvent):void {
	navigator.popView();
}

override protected function applyData():void {
	trace("applyData() SecondScreen");
	if (!data)
		return;
	lbl.text = data["label"];
}

In case you’re wondering how do I save the data, I’m using SharedObject and I persist an array representing the current screen stack (the screen class name and the data object for each instance).

Conclusions

Although I intend to use this library in my projects, I haven’t tested it extensively ywt. This means you may find bugs. If so, please let me know. Furthermore, there is a big difference in how this library manages the views compared to how Flex does: Flex’s ViewNavigator destroys by default a view when removing it. This library doesn’t. So, if you have lots of views with lots of components you might end up using a lot of memory. For such a use case it should be modified to mimic the Flex approach.

If you’ve started to create apps for PlayBook I’m eager to learn how you manage the screens. So, please talk back :D!

14 thoughts on “Support for screen navigation and application session caching for QNX PlayBook apps

  1. Wow, this is really useful Mihai. However, I will stick to creating Flex based apps for now just because the QNX containers are a real pain.

  2. Pingback: Tweets that mention Support for screen navigation and application session caching for QNX PlayBook apps : Mihai Corlan -- Topsy.com

  3. Hi,
    it’s pretty cool!
    why don’t you fork the as3 navigator project on github and add your features ? It could be a nice way create a base project for any AS3 only mobile project :)

    Lionel

  4. Great stuff!!! am i the only one with the new playbook vm on windows, i have a mega pc, and i worked with the previous version , but cant get the emulator running on windows,it hangs at the boot screen, not even with a static 30 gig hd and 2 gig mem. so i tried going back to the previous iso and it worked like a charm, only i patched/merged flashbuilder and hero with the new sdk and now i cant get it to work, it is driving me nuts.. it goes wrong with the signing, i create an p12 , i create an developper CSK , but i cant get it signed, it asks to refer to the location of the CSJ file , i point that one to my App/local/research in motion.. dir , but nothing works, think that it just doesnt work , could you confirm that, would i be able to use the new sdk on the old playbook and sign my bar file…i dont have the old sdk anymore so i am kind of stuck been playing with pioter’s stuff and ur kewl extensions would love to see it on the playbook.
    I am guessing u are using Mac ?
    thanks again!!

  5. Pingback: Cool Stuff with the Flash Platform – 2/25/11 | Finding Out About

  6. I am trying to turn off caching by setting the ViewNavigator sessionCacheEnabled to false but get the following error trying to run the app:

    TypeError: Error #1007: Instantiation attempted on a non-constructor.

    On line 183 of …
    \org\corlan\asviews\ViewNavigator.as

    My line 183 is …
    var view:IView = new factory() as IView;

    Seems like the homeScreen class is empty but where do i set it? Can anyone pls help?
    (hoping this is not a stupid question)

  7. Have you added the first screen? Something along the lines:

    navigator = new ViewNavigator(this, true);
    // Pushing first view to the navigator
    navigator.pushView(HomeScreen);

  8. Hi, i want to implement view navigator logic on a popup. I have a button called settings and clicking on that I’m creating a popup with different items in it. Clicking on each item inside the popup the present view should be pushed and clicked view should come up.
    Im using this is in Qnx container.Can any one please help me how can we implement push and pop with out using view navigator application
    Thanks

  9. @raja

    Why you couldn’t use it for commercial apps? Of course you can use it…

    Mihai Corlan

  10. Hi Mihai Many thanks for quick response…my collegue said the license says if you use AS3 view navigator its like some thing we need to make our code available to public….

    i want to implement view navigator logic on a popup. I have a button called settings and clicking on that I’m creating a popup with different items in it. Clicking on each item inside the popup has to push another container inside the popup..

    Im using this is in Qnx container.Can you please help me how can we implement push and pop logic with out using view navigator.

    Many Thanks,
    Raja.

  11. Hi Mihai, Really thanks for the post and its so helpful for me.

    Im working on the magazine application, and my manager is more worried about CONSEQUENCES on using the AS3 View navigator as it is in GPL. (My understanding is anyone using your code has to make their source code available on demand ) So can you please help me out how to move further…

    Is there any other way to implement push and pop logic with out using view navigator??

    Many Thanks,
    Raja.

  12. @raja

    I’m afraid you got it wrong. Although the Flex framework is Open Source, the license doesn’t force you have to open your apps based on the Flex. So, yes you can use it for commercial apps.

    Besides this, what I created here for QNX it has nothing to do with the Flex framework thus it has nothing to do with the Flex license.

    hope this clear the things,
    Mihai

Leave a Reply

Your email address will not be published. Required fields are marked *