Installing Your Signed App onto the PlayBook

You can send your signed app to the BlackBerry PlayBook using blackberry-dploy. This makes it easy to test final builds without having to setup debugging certificates. We’ll talk about debug certificates in a later post, let’s get our app onto the device!

Problem:
You want to test your signed application on the device before submitting it to the app world or setting up the debug certificate. The PlayBook browser doesn’t currently support the BAR extension (hopefully this will change).

Solution:
Use the blackberry-deploy command to send your signed BAR file directly to the device. Before doing this you’ll need to put your device in developer mode.

To put your PlayBook in developer mode go into the device settings (top right corner) and select Security. Click on the Development Mode button and select to enable it. Here’s the tricky part, to actually get this working I needed to reboot the device and go back into the settings and re-enable development mode. This might have just been a fluke but worth mentioning. After doing this the developer icon showed up on the home screen along with my device ip address (see image).

Connect the device up to your machine using the usb cable, open a command prompt in the SDK bin folder and run the following command:

>blackberry-deploy -installApp -device DEVICE_IP -password DEV_PASSWORD yourappname.bar

You will have to put the full path to your BAR if it’s not in the bin directory.

After running that command my signed application was on the device and ready to go.

iOS, Android and BlackBerry in a Single Click with ANT

***Update: Use package.blackberry OR install.blackberry when running the ANT task, using both will overwrite the BAR file and remove signing. Verify your BAR is signed by renaming it from app.bar to app.zip, extract and ensure you have META-INF/AUTHOR.EC and META-INF/RDK.EC included in the zip.***

Create your market ready AIR apps for iOS, Android and the BlackBerry PlayBook in a single click using the same code. This ANT task compiles, signs and pushes out to attached devices (iOS requires dragging into iTunes). All of the signed apps created with this ANT task can be published to the market.

Overview:
The AIR 2.6 SDK is great but doesn't have full Flash Builder support yet. This ANT task can be used in Flash Builder, Flash Develop, FDT or even the command line. Using Flash Develop, you can create apps for all devices without paying for expensive software!

Pre-requisites:
You will need to install Flash Builder Burrito, the BlackBerry PlayBook SDK, the AIR 2.6 SDK and acquire signing keys for all three devices. If using Flash Develop, you will need to follow some additional steps for ANT integration. You'll also want to download the base project from Google Code: http://code.google.com/p/air-mobile-tools/

Video Tutorial:

Wiki:
The wiki on Google code contains more information on getting started with this base project: http://code.google.com/p/air-mobile-tools/wiki/BaseMobileActionScriptProject

ANT Task:

XML:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--
  3. MIT License:
  4. Copyright (c) 2010 Christopher Black
  5. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  6. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  8. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9. Resources:
  10. http://www.terrenceryan.com/blog/post.cfm/using-ant-to-package-the-same-air-app-to-multiple-devices
  11. http://technophi.com/2011/03/08/using-ant-to-compile-a-flex-mobile-project-for-ios/
  12. http://www.adobe.com/devnet/flex/articles/flex_ant_pt1.html
  13. http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-7ffb.html
  14. -->
  15. <project name="Box2D" default="main" basedir="."><property name="environment" value="local" /><property file="settings.properties"/>
  16.  
  17. <!-- Path to the Flex task libraries. -->
  18. <path id="flextasks.classpath">
  19.   <fileset dir="${FLEX_HOME}/ant/lib">
  20.     <include name="*.jar"/>
  21.   </fileset>
  22. </path>
  23. <typedef resource="flexTasks.tasks" classpathref="flextasks.classpath" />
  24.  
  25. <!-- Add dependencies here (android, blackberry, apple) or uninstall.blackberry -->
  26. <target name="main" depends="android, blackberry, apple" />
  27. <target name="android" depends="prepPackage, package.android, install.android" />
  28. <!-- Use package.blackberry OR install.blackberry, using both will overwrite the .bar file and remove signing. -->
  29. <target name="blackberry" depends="prepPackage, package.blackberry" />
  30. <target name="apple" depends="prepPackage, package.apple" />
  31. <target name="clean">
  32.   <echo message="Cleaning Build Space"/>
  33.   <delete dir="${build.dir}"/>
  34. </target>
  35. <target name="prepPackage" depends="compile,handleDevices" />
  36. <target name="compile" depends="clean">
  37.   <echo message="Compiling swf"/>
  38.   <mxmlc file="${projectFile}" output="${swfFile}" optimize="true" configname="airmobile" debug="false">
  39.     <load-config filename="${FLEX_HOME}/frameworks/airmobile-config.xml"/>
  40.     <source-path path-element="${FLEX_HOME}/frameworks"/>
  41.     <static-link-runtime-shared-libraries />
  42.     <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
  43.       <include name="libs/*" />
  44.     </compiler.library-path>
  45.     <compiler.library-path dir="${basedir}/libs" append="true">
  46.       <include name="*" />
  47.     </compiler.library-path>
  48.   </mxmlc>
  49. </target>
  50.  
  51. <!-- Create Android directory. -->
  52. <target name="collect.android">
  53.   <echo message="Creating device folder for Android"/>
  54.   <mkdir dir="${build.dir}/android"/>
  55.   <echo message="Copying SWF for Android"/>
  56.   <copy file="${cert.dir}/androidcert.p12" todir="${build.dir}/android" />
  57.   <copy file="${swfFile}" todir="${build.dir}/android" />
  58.   <echo message="Copying Application Description File for Android"/>
  59.   <copy file="${dev.dir}/${app.name}-app.xml" todir="${build.dir}/android" preservelastmodified="true" />
  60.   <copy todir="${build.dir}/android/assets">
  61.     <fileset dir="${dev.dir}/assets" />
  62.   </copy>
  63.   <echo message="Modifying application description file"/>
  64.   <replace file="${build.dir}/android/${app.name}-app.xml">
  65.     <replacefilter token="${contentText}" value="${app.name}.swf" />
  66.   </replace>
  67. </target>
  68.    
  69. <!-- Create BlackBerry directory. -->
  70. <target name="collect.blackberry">
  71.   <echo message="Creating Device Folder for BlackBerry"/>
  72.   <mkdir dir="${build.dir}/blackberry"/>
  73.   <echo message="Copying SWF for BlackBerry"/>
  74.   <copy file="${swfFile}" todir="${build.dir}/blackberry" />
  75.   <copy file="${cert.dir}/${bb.cert}" todir="${build.dir}/blackberry" />
  76.   <echo message="Copying Application Description File for BlackBerry"/>
  77.   <copy file="${dev.dir}/${app.name}BlackBerry-app.xml" todir="${build.dir}/blackberry" preservelastmodified="true" />
  78.   <copy file="${dev.dir}/blackberry-tablet.xml"
  79.     todir="${build.dir}/blackberry" preservelastmodified="true" />
  80.   <copy todir="${build.dir}/blackberry/assets">
  81.     <fileset dir="${dev.dir}/assets" />
  82.   </copy>
  83.   <echo message="Modifying application description file"/>
  84.   <replace file="${build.dir}/blackberry/${app.name}BlackBerry-app.xml">
  85.     <replacefilter token="${contentText}" value="${app.name}.swf" />
  86.   </replace>
  87. </target>
  88.    
  89. <!-- Create Apple directory. -->
  90. <target name="collect.apple">
  91.   <echo message="Creating device folder for apple"/>
  92.   <mkdir dir="${build.dir}/apple"/>
  93.   <echo message="Copying SWF for Apple"/>
  94.   <copy file="${swfFile}" todir="${build.dir}/apple" />
  95.   <copy file="${cert.dir}/${apple.cert}" todir="${build.dir}/apple" />
  96.   <copy file="${cert.dir}/${apple.provision}" todir="${build.dir}/apple" />
  97.   <echo message="Copying Application Description File for Apple"/>
  98.   <copy file="${dev.dir}/${app.name}Apple-app.xml" todir="${build.dir}/apple" preservelastmodified="true" />
  99.   <copy todir="${build.dir}/apple/assets">
  100.     <fileset dir="${dev.dir}/assets" />
  101.   </copy>
  102.   <echo message="Modifying Application Description File"/>
  103.   <replace file="${build.dir}/apple/${app.name}Apple-app.xml">
  104.     <replacefilter token="${contentText}" value="${app.name}.swf" />
  105.   </replace>
  106. </target>
  107.    
  108. <target name="handleDevices" depends="collect.android, collect.blackberry, collect.apple"/>
  109. <target name="package.android">
  110.   <echo message="Packaging for Android ${cert}"/>
  111.   <exec executable="${ADT}" dir="${build.dir}/android">
  112.     <arg value="-package"/>
  113.     <arg line="-target apk"/>
  114.     <arg line="-storetype pkcs12"/>
  115.     <arg line="-keystore ${cert}" />
  116.     <arg line="-storepass ${cert.password}" />
  117.     <arg value="${app.name}"/>
  118.     <arg value="${app.name}-app.xml"/>
  119.     <arg value="${app.name}.swf"/>
  120.     <arg line="assets" />
  121.   </exec>
  122. </target>
  123. <target name="package.blackberry">
  124.   <echo message="Packaging for BlackBerry"/>
  125.   <exec executable="${BBPackager}" dir="${build.dir}/blackberry">
  126.     <arg value="-package"/>
  127.     <arg value="${app.name}.bar"/>
  128.     <arg value="${app.name}BlackBerry-app.xml"/>
  129.     <arg value="blackberry-tablet.xml"/>
  130.     <arg value="${app.name}.swf"/>
  131.     <arg line="assets" />
  132.   </exec>
  133.   <exec executable="${BBSigner}" dir="${build.dir}/blackberry">
  134.     <arg value="-verbose"/>
  135.     <arg line="-cskpass ${bb.cert.password}"/>
  136.     <arg line="-keystore ${bb.cert}"/>
  137.     <arg line="-storepass ${bb.store.password}"/>
  138.     <arg value="${app.name}.bar"/>
  139.     <arg value="RDK" />
  140.   </exec>
  141.   <exec executable="${BBSigner}" dir="${build.dir}/blackberry">
  142.     <arg line="-keystore ${bb.cert}"/>
  143.     <arg line="-storepass ${bb.store.password}"/>
  144.     <arg value="${app.name}.bar"/>
  145.     <arg value="author" />
  146.   </exec>
  147. </target>
  148. <target name="package.apple">
  149.   <exec executable="${ADT}" dir="${build.dir}/apple">
  150.     <arg line="-package -target '${apple.target}'" />
  151.     <arg line="-provisioning-profile '${apple.provision}'" />
  152.     <arg line="-storetype pkcs12" />
  153.     <arg line="-keystore '${apple.cert}'" />
  154.     <arg line="-storepass '${apple.cert.password}'" />
  155.     <arg line="'${app.name}.ipa'" />
  156.     <arg line="'${apple.descriptor}'" />
  157.     <arg line="'${app.name}.swf'" />
  158.     <arg line="assets" />
  159.   </exec>
  160. </target>
  161. <target name="install.android">
  162.       <echo message="Uninstalling attached Android Device"/>
  163.       <exec executable="${ADT}">
  164.         <arg line="-uninstallApp"/>
  165.         <arg line="-platform android"/>
  166.         <arg line="-appid '${app.name}'"/>
  167.       </exec>
  168.   <echo message="Installing onto attached Android Device"/>
  169.   <exec executable="${ADT}">
  170.     <arg line="-installApp"/>
  171.     <arg line="-platform android"/>
  172.     <arg line="-package '${build.dir}/android/${app.name}.apk'"/>
  173.   </exec>
  174.   <echo message="Launching on attached Android Device"/>
  175.   <exec executable="${ADT}">
  176.     <arg line="-launchApp"/>
  177.     <arg line="-platform android"/>
  178.     <arg line="-appid '${app.name}'"/>
  179.   </exec>
  180. </target>
  181. <target name="uninstall.blackberry">
  182.   <echo message="Uninstalling from Blackberry VM"/>
  183.   <exec executable="${BBDeploy}" dir="${build.dir}/blackberry">
  184.     <arg value="-uninstallApp"/>
  185.     <arg value="-device"/>
  186.     <arg value="${bb.ip}"/>
  187.     <arg value="-password"/>
  188.     <arg value="${bb.password}"/>
  189.     <arg value="-package"/>
  190.     <arg value="${app.name}.bar"/>
  191.   </exec>
  192. </target>
  193. <target name="install.blackberry">
  194.   <echo message="Installing onto Blackberry VM"/>
  195.   <exec executable="${BBPackager}" dir="${build.dir}/blackberry">
  196.     <arg value="-package"/>
  197.     <arg value="${app.name}.bar"/>
  198.     <arg value="-installApp"/>
  199.     <arg value="-launchApp"/>
  200.     <arg value="${app.name}BlackBerry-app.xml"/>
  201.     <arg value="blackberry-tablet.xml"/>
  202.     <arg value="${app.name}.swf"/>
  203.     <arg value="-device"/>
  204.     <arg value="${bb.ip}"/>
  205.     <arg value="-password"/>
  206.     <arg value="${bb.password}"/>
  207.     <arg value="assets"/>
  208.   </exec>
  209. </target>
  210. </project>

Actionscript:
  1. flex.path = C:/Program Files (x86)/Adobe/Flash Builder Burrito/Adobe Flash Builder Burrito/sdks
  2. flex.sdkVersion= 4.5.0_air
  3. flex.sdkPath= ${flex.path}/${flex.sdkVersion}
  4. FLEX_HOME= ${flex.sdkPath}
  5. BB_HOME = ${flex.path}/blackberry-tablet-sdk-0.9.4
  6. contentText = [This value will be overwritten by Flash Builder in the output app.xml]
  7.  
  8. ADB = C:/Users/Chris/Desktop/Froyo/android-sdk-windows/platform-tools/adb.exe
  9. ADT = ${flex.sdkPath}/bin/adt.bat
  10. adt.path = ${flex.sdkPath}/bin/
  11. IPHONE_PACKAGER = adt
  12.  
  13. BBDeploy = ${flex.path}/blackberry-tablet-sdk-0.9.4/bin/blackberry-deploy.bat
  14. BBPackager = ${flex.path}/blackberry-tablet-sdk-0.9.4/bin/blackberry-airpackager.bat
  15. BBSigner = ${flex.path}/blackberry-tablet-sdk-0.9.4/bin/blackberry-signer.bat
  16. bb.ip = [PLAYBOOK_IP]
  17. bb.password = [YOUR_PASSWORD]
  18.  
  19. ## Directories
  20. build.dir = bin-release
  21. dev.dir = ${basedir}/src
  22. cert.dir = ${basedir}/certs
  23. app.name = BaseMobileActionScriptProject
  24.  
  25. ## mxml or as
  26. app.type = as
  27.  
  28. ## Apple cert info: ipa-test | ipa-debug | ipa-app-store | ipa-ad-hoc
  29. apple.target = ipa-ad-hoc
  30. apple.cert = [APPLE_CERT_NAME].p12
  31. apple.cert.password = [CERT_PASSWORD]
  32. apple.provision = [PROVISION_NAME].mobileprovision
  33. apple.descriptor = ${dev.dir}/${app.name}Apple-app.xml
  34.  
  35. ## Android cert info
  36. cert = [ANDROID_CERT_NAME].p12
  37. cert.password = [YOUR_PASSWORD]
  38.  
  39. ## PlayBook cert info
  40. bb.cert = [BB_CERT_NAME].p12
  41. bb.cert.password = [YOUR_PASSWORD]
  42. bb.store.password = [YOUR_STORE_PASSWORD]
  43. bb.descriptor = ${dev.dir}/${app.name}BlackBerry-app.xml
  44.  
  45. ## General
  46. swfFile=${build.dir}/${app.name}.swf
  47. projectFile=${dev.dir}/${app.name}.${app.type}

Resources:
http://www.terrenceryan.com/blog/post.cfm/using-ant-to-package-the-same-air-app-to-multiple-devices
http://technophi.com/2011/03/08/using-ant-to-compile-a-flex-mobile-project-for-ios/
http://www.adobe.com/devnet/flex/articles/flex_ant_pt1.html
http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-7ffb.html

E4X XML Cheat Sheet

Parsing XML is one of the major pain points in development. Without exact syntax you're left scratching your head. After visiting the same collection of links over and over, I've decided to consolidate the more useful syntax into a single post. All examples can be used with both JavaScript and ActionScript as they are both based off of ECMAScript.

Problem:
E4x is great but it requires exact knowledge of the syntax. It's easy to forget common functions if not used every day.

Solution:
This cheat sheet is a collection of examples from common XML structures. It's meant to kick your brain back into gear after a few months without working in e4x. For a more comprehensive guide to XML and e4x please visit the links at the bottom of this post.

What is XML and e4x? I'll let Wikipedia handle that one.

Step 1: Define XML

Here is a quick example on defining XML inline:

Actionscript:
  1. var data:XML = <data>
  2.     <image>pirates.jpg</image>
  3. </data>;

JavaScript:
  1. var data = <data>
  2.     <image>pirates.jpg</image>
  3. </data>;

The only difference is that the ActionScript variable is typed. The JS version will work in AS but typed variables are so much easier to work with. Code completion is my best friend.

OK, from here on out I'm just going to show the XML, trace statements and expected output. Everything is cast to a string for easy output.

Step 2: Access a node value

Using the XML above...

Actionscript:
  1. trace(String(data.image));
  2.  
  3. Output:
  4. pirates.jpg

JavaScript:
  1. alert(String(data.image));

Strikingly similar, however, break points and trace statements are so much more fun than alerts.

Step 3: Access an attribute

XML:
  1. <data>
  2.     <image name="Pirate Image">pirates.jpg</image>
  3. </data>

Actionscript:
  1. trace(String(data.image.@name));
  2.  
  3. Output:
  4. Pirate Image

I assume by this point you can figure out the JS syntax. Writing JS is like eating squash. It's good for you but why do it when there is free cake?

Step 4: Access a node value given an attribute key

XML:
  1. <data>
  2.     <image name="Pirate Image" id="p1">pirates.jpg</image>
  3.     <image name="Pirate Image 2" id="p2">pirates2.jpg</image>
  4. </data>

Actionscript:
  1. trace(String(data.image.(@id == "p2")));
  2.  
  3. Output:
  4. pirates2.jpg

JavaScript developers are ActionScript developers, they just don't know it yet.

Step 5: Access an attribute given an attribute key

Using the XML from step 4...

Actionscript:
  1. trace(String(data.image.(@id == "p2").@name));
  2.  
  3. Output:
  4. Pirate Image 2

ActionScript and JavaScript are like two people that hate each other only to later find out that they are actually siblings with everything in common. They proceed to live happily ever after.

Step 6: Parse through nodes

XML:
  1. <data>
  2.     <image name="Pirate Image" id="p1">pirates.jpg</image>
  3.     <image name="Flower Image" id="f1">flower.jpg</image>
  4.     <image name="Car Image" id="c1">car.jpg</image>
  5. </data>

Actionscript:
  1. for each(var img:XML in data.image)
  2. {
  3.     trace(String(img.@name));   
  4. }
  5.  
  6. Output:
  7. Pirate Image
  8. Flower Image
  9. Car Image

Just remove the typed variable, replace trace with print and your JS will be good to go. At this point you might want to start populating value objects with attributes, paths and id's.

Additional resources:
http://www.republicofcode.com/tutorials/flash/as3xml/
http://www.senocular.com/flash/tutorials/as3withflashcs3/?page=4

JSON parsing cheat sheet coming soon!

Link Directly to the Mobile Android Market

With one line of code you can open up the mobile Android market from within your app! You can open the market to a specific application or to a page listing all of your apps. This is a great way to cross sell your applications and limit the number of clicks for a user to download your app.

Problem:
Linking to the Android market on a mobile web browser brings up a security warning saying "There are problems with the security certificate for this site." This pop up is rather troublesome, who is actually going to click to continue? Even if they do the web interface doesn't display very well on mobile devices. Let's bypass this message all together and bring the user directly to the Android market app within the mobile device!

Solution:
Replace "https://market.android.com/" with "market://" for any valid market URL. It's that easy! Just make sure to do this only within mobile applications. Desktop browsers and any other non-Android device will not know what to do with the "market://" preface. On the desktop use the standard URL and within mobile devices use the shorthand URL. This will ensure the best user experience for everyone and ensure the most downloads of your app.

Use this line of code in your AIR for Android Apps to bring up a search result (example 'terry paton'):
navigateToURL(new URLRequest("market://search?q=terry+paton"));

or link directly to an app with this:
navigateToURL(new URLRequest("market://details?id=air.com.terrypaton.tc2"));

Navigating to "https://market.android.com/..." on an Android brings up this message:
Security warning message.

However, navigating to "market://search?q=terry+paton" brings the user to the market!
Terry Paton on the Android Market

A big thanks to Terry Paton for working with me on my mobile marketing campaign. Make sure to check out all of his awesome AIR for Android games on the market:
https://market.android.com/search?q=terry+paton