Tony Lukasavage

Caffeine. Whiskey. Code. Mostly the last one.

From Air for Android to Blackberry Playbook in seconds

Overview

NOTE: all source code is available for Repper at Github.

The above image shows my Air for Android app Repper ported over to the Blackberry Playbook.  The most remarkable thing about this port? It literally took about 60 seconds to complete!  Here is the shockingly simple, comprehensive list of steps I took to accomplish this:

  1. Create a new Flex mobile project in Flash Builder “Burrito” to target the Blackberry Playbook
  2. Copied the project files from my original Repper project to the new project
  3. Set up the Playbook simulator run settings in my new project
  4. Clicked “Run” and voila, Repper on the Playbook!

All components scaled properly.  All layouts fit to the available screen space appropriately.  All orientation transitions performed as expected.  How was this achieved without having to write a single line of device specific code?  It comes down to 3 simple things: Adobe Air, fluid layouts, and vector graphics skinning via Flex MXML.

Adobe Air

Blackberry’s use of the Adobe Air SDK for its Playbook development lets Flash/Air/Flex/AS3 developers leverage existing expertise and code to create apps.  The Air SDK for the Playbook also makes available QNX components to give Playbook apps a Blackberry look and feel.  You can choose from either.  This way Flex developers can use their existing paradigms and pure AS3 developers can make use of Blackberry’s QNX components.  You can even use both as Renaun Erickson details in this post.

For me the big win was taking Repper, a Flex “Hero” app for Android, and porting it directly, unchanged into a working Playbook app.  The Playbook Adobe Air SDK supports Flex “Hero” so everything just worked right out of the gate.  That’s the kind of portability that lets an independent developer be extremely productive.

Fluid Layout

Using fluid layouts with little to no defined pixel dimensions is key to writing a multi-screen app.  Take a look at this excerpt from the main RepperHome.mxml view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    <s:HGroup id="landscapeGroup" includeIn="landscape" height="100%" width="100%"
        paddingTop="{this.height*0.03}" paddingBottom="{this.height*0.03}" paddingLeft="{this.height*0.03}"
        paddingRight="{this.height*0.03}" gap="{this.height*0.03}" verticalAlign="middle">
  <s:SkinnableContainer id="landscapeInput" width="36%" skinClass="RepperInputSkin">
      <s:layout>
          <s:VerticalLayout paddingTop="{this.height*0.06}" paddingLeft="{this.height*0.06}"
                            paddingRight="{this.height*0.06}" paddingBottom="{this.height*0.06}"
                            gap="{landscapeInput.height*0.04}" horizontalAlign="right"/>
      </s:layout>
      <s:HGroup width="100%" verticalAlign="middle" gap="0">
          <s:Label text="Weight" width="65%" color="0x000000" fontWeight="bold" fontSize="32"/>
          <s:TextInput width="35%" text="{inputWeight}" keyUp="onWeightKeyUp(event)" restrict="0-9."
                       contentBackgroundColor="0xffffff" color="0x000000"/>
      </s:HGroup>
      <s:HGroup width="100%" verticalAlign="middle" gap="0">
          <s:Label text="# of reps" width="65%" color="0x000000" fontWeight="bold" fontSize="32"/>
          <s:TextInput width="35%" text="{inputReps}" keyUp="onRepsKeyUp(event)" restrict="0-9"
                       contentBackgroundColor="0xffffff" color="0x000000" focusIn="textinput1_focusInHandler(event)"/>
      </s:HGroup>
      <s:Button label="Rep It" click="recalculate();" chromeColor="#111111" />
  </s:SkinnableContainer>
  <local:VDataGroup width="32%" height="100%" dataProvider="{repValues1}" itemRenderer="RepperEntry"/>
  <local:VDataGroup width="32%" height="100%" dataProvider="{repValues2}" itemRenderer="RepperEntry"/>
</s:HGroup>

If you look at every component and container in this chunk of Flex MXML you’ll notice that no defined pixel dimensions are used. Every component’s size is determined using percentages or data bindings. I’m not going to lie, specifying dimensions in this manner will take more time and requires a bit of trial and error to get right. It helps if you do it with the expectation that another, dissimilar device will be using this same layout in the future. I did, and in this case the little bit of extra forethought paid off huge in terms of portability.

Because of design via fluid layouts, Repper appears properly in both portrait and landscape orientation on the Blackberry Playbook with no additional effort.

Vector Graphics via Flex MXML Skinning

The final factor that made Repper such a breeze to port to the Playbook was Flex Hero’s ability to create vector graphics via Flex MXML. By utilizing this capability you can create skins for your components that scale as the app scales. No external artwork of any kind is necessary. While this is not ideal for designers or highly stylized apps, a developer like me loves it. Here’s another excerpt from Repper. This time it shows how I used fluid dimensions once again to create vector graphic skins for my RepperEntry components via the RepperEntrySkin.mxml class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<s:Rect id="background" x="0" y="{this.height/6}" height="66%" width="100%" radiusX="10" radiusY="10">
<s:filters>
  <s:DropShadowFilter blurX="16" blurY="16"/>
</s:filters>
<s:stroke>
  <s:SolidColorStroke weight="3"/>
</s:stroke>
    <s:fill>
  <s:LinearGradient rotation="-90">
      <s:GradientEntry color="#bbbbbb"/>
      <s:GradientEntry color="#ffffff"/>
        </s:LinearGradient>
    </s:fill>
</s:Rect>
<s:Rect x="0" y="{this.height/6}" height="66%" width="60%" radiusX="10" radiusY="10">
<s:filters>
  <s:DropShadowFilter blurX="8" blurY="8" angle="0"/>
</s:filters>
<s:stroke>
  <s:SolidColorStroke weight="3"/>
</s:stroke>
<s:fill>
  <!--- @private -->
  <s:LinearGradient rotation="-90">
      <s:GradientEntry color="#000000"/>
      <s:GradientEntry color="#444444"/>
  </s:LinearGradient>
  <!-- <s:SolidColor color="#333333"/> -->
</s:fill>
</s:Rect>

As you can see, percentages and binding are used to create skins for my components that are appropriately scaled regardless of the dimensions of the device. Not having to create skin resources for each device and orientation is a tremendous time saver. Especially for someone like myself who has no patience or talent for it.

Summary

Lesson for the day: with a little forethought and a penchant for Adobe Air, magic can happen. OK, maybe not magic, but you’ll have more time to explore the possibility of creating magic now that you won’t be wasting it on porting your mobile apps. Yes, I know, I’ve griped a bit lately about Flex Hero on twitter, but there are undoubtedly some things it does very well.

In fact, if you think about it, anyone that openly supports Adobe and their efforts to create a viable cross-platform mobile development environment has done very well with it. I’m trying to think of who isn’t supporting it? Who’s at the “core” of the issue? I feel like day to day they are trying to keep the doctor away. Supporting Adobe should be as appealing as American pie. Oh well, hopefully some day they will have their thinking jolted like Isaac Newton did from a falling piece of fruit.