How to Win Prizes at a Hackathon with Sencha Touch 2.2 Theming

A couple of weeks ago myself and one of my fellow Figs, Jason Perry, attended a MoDevUX Hackathon. Honestly, I had no idea what to expect – the last programming competition that I was in was when I led my high school team to a 12th place finish among all high schools in the area as we typed fast and furiously on an Apple IIE, programming in BASIC.

Obviously, the technology has improved just a little bit since then – as has the competition. After all, several of the hackathon contestants were just a glimmer in their parent’s eyes back in 1987 when going “mobile” meant lugging 20 lbs of equipment around with a handtruck. But I digress…

The goal of the hackathon was to produce a mobile app that had something to do with finance. (Capital One was the big sponsor). You got bonus points for developing apps for Microsoft Windows phones (Nokia was a sponsor), as well as using various APIs (Mashery.com) and/or adding VOIP/Voice/SMS communications to an app using the awesome Twilio API.

walletA third team member, whom we met at the Hackathon, came up with the idea to produce a mobile/social app that would enable people to exchange international currencies at the airport, without having to pay the ridiculous transaction fees charged by the on-site money exchanges.

My strategy was to cover as many bases as possible to ensure that we’d win *something* – I figured a first-time win for Team Fig would go a long way towards boosting company morale (and I wanted a Microsoft phone to complete our suite of  test devices). So, with the immortal words of Sean Connery ringing in my ear, we set off to design and build an app that would support the following features:

1) Look and feel like a native Windows Mobile phone (as well as be compatible across a broad range of other mobile devices)
2) Integrate voice/sms features from Twilio
3) Calculate currency conversion based on the current exchange rates via a third-party API
4) Link to a back-office database that would help facilitate matching compatible requests.
5) Translate requests and messages into different languages via the Google Translate API

And get functional prototype programmed and tested within a seven hour timeframe on a device (Nokia Windows Phone) that we had no prior development experience with. What could possibly go wrong?

Divide and Conquer

In order to make the deadline, we decided to use Sencha Touch 2.2 as our underlying development platform as they had just begun supporting Windows Mobile devices and had a Windows-Mobile compatible theme ready to go “out of the box.” I was responsible for developing the “front-end” Touch UI, while Jason was responsible for standing up a publicly accessible XAMPP stack and all the “back-end” API integration (PHP, figuring out how to use the Twilio API, etc).

Supporting Windows Mobile Devices

Supporting Windows Mobile 8 was the lynchpin of our strategy. I figured that since it was relatively new, most hackathon participants wouldn’t know how to develop for it. And even if they were familiar with it, they probably wouldn’t be able to throw an app together with Microsoft’s native tools as quickly as we could by using Sencha Touch.

Supporting Windows Mobile in Sencha Touch really comes down to linking to the “out of the box” Windows mobile stylesheet. In a Sencha Touch project, this can be accomplished by just typing a few lines out into the app.json file, which controls the loading of stylesheets based on device platform:

"css": [
 {
  "platform": ['ie10'],
  "theme": "Windows",
  "update": "delta",
  "path": "resources/css/ie.css"
 },
 {
  "path": "resources/css/bb.css",
  "platform": ['blackberry'],
  "theme": "Blackberry",
  "update": "delta"
 },
 {
   "path": "resources/css/default.css",
   "platform": ['chrome', 'safari', 'ios'],
   "theme": "Default",
   "update": "delta"
 }
]

The CSS file itself is generated by SASS/Compass, from a .SCSS file that resembled the following:

@import ‘sencha-touch/windows’;
@import ‘sencha-touch/windows/all’;

@include icon(‘upload’);
@include icon(‘left’);

So, with about 10 lines of code, we were able to leverage years of Sencha Touch development experience to produce an app for a device that we had never actually laid our hands on. We could also use Sencha Architect to rapidly prototype the UI, which was an added bonus!

Integrating Currency Conversion

Integrating currency conversion into an application is a pretty straightforward affair. We used a free currency rate service that supported a JSON-P interface that could be called directly from our Sencha Touch app:

Ext.define('MyApp.util.Currency', {
 statics: {
  conversionRate: function( from, to, callback, scope) {
   Ext.data.JsonP.request({
     url: 'http://rate-exchange.appspot.com/currency',
     params: {
      from: from,
      to: to
     },
     success: function(response) {
	callback.call(scope, response);
     }
   });
  }
 }
});

In this case, from and to represent currency codes (e.g. USD is US Dollars).

Using the Google Translate API

One of the features that we added to the app was the ability to send SMS and voice-synthesized phone messages (via Twilio), translated into the language of the recipient. Google Translate, while far from perfect, is quite easy to use – albeit not a free service. Integrating it into Sencha Touch was a snap:

Ext.define('MyApp.util.Translate', {
 statics: {
  key: 'AIza...',
  translate: function(text, source, target, callback, scope) {
   if (source == target) {
    callback.call(scope,text);
   } else {
    Ext.data.JsonP.request({
      url: 'https://www.googleapis.com/language/translate/v2',
      params: {
	key: MyApp.util.Translate.key,
	source: source,
	target: target,
	q: text
      },
      success: function(response) {
        callback.call(scope, response.data.translations[0].translatedText);
      }
    });
   }
  }
 }
});

Adding tel/sms services using Twilio

Twilio is a cloud-based service that enables you to create apps which send/receive/route phone calls, record audio, capture stats, send SMS messages, and more. They’ve even got an HTML5 SDK and series of REST services that you can invoke from Sencha Touch. We designed the app so that a user could message someone via SMS and voice-synthesized message (in language) who had a reciprocal currency exchange need at the designated airport, while keeping the user’s telephone numbers private.

In order to accomplish this, we defined a static class in our app that invoked a couple of custom webservices that we developed initially in PHP, and then later ported over to ColdFusion:

Ext.define('MyApp.util.Twilio', {
 statics: {
  sendText: function( transactionId, msg, callback, scope) {
    Ext.Ajax.request({
      url: 'http://webapps.figleaf.com/wallet/transaction.cfc?method=sendText',
      params: {
	id: transactionId,
	msg: msg
      },
      success: function(response) {
	callback.call(scope, response);
      }
    });
  },

  sendCall: function( transactionId, msg, callback, scope) {
    Ext.Ajax.request({
      url: 'http://webapps.figleaf.com/wallet/transaction.cfc?method=sendCall',
      params: {
	id: transactionId,
	msg: msg
      },
      success: function(response) {
	callback.call(scope, response);
      }
   });
  }
 }
});

On the server-side, we lookup transaction details and then invoke Twilio to send SMS messages and make the phone call. The way that Twilio scripted phone calls work is that you pass a callback URL that generates “TwiML” to their service. TwiML is quite powerful, but for our limited proof of concept, all we needed to do was tell it to “read” from a script that we translated into language using the Google Translate API.

<!--- send an sms text message --->
<cffunction name="sendText" access="remote" returntype="string" returnformat="plain">
 <cfargument name="id" type="numeric" required="yes">
 <cfargument name="msg" type="string" required="Yes">

 <cfset var q = "">
 <cfset var stSuccess={}>
 <cfquery name="q">
  select * from transaction where transactionid=<cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.id#">
 </cfquery>
 <cfhttp
   username="[twilio user id]"
   password="[twilio password]"
   url="https://api.twilio.com/2010-04-01/Accounts/[twilio account id]/SMS/Messages" method="post">
     <cfhttpparam name="From" value="[twilio phone num]" type="formfield">
     <cfhttpparam name="To" value="#q.phone#" type="formfield">
     <cfhttpparam name="Body" value="#arguments.msg#" type="formfield">
 </cfhttp>
 <cflog file="twilio" text="#cfhttp.FileContent#">
 <cfset stSuccess= {success: true}>
 <cfreturn serializeJson(stSuccess)>
</cffunction>

<!--- generate TwiMl --->
<cffunction name="playMsg" access="remote" returntype="xml" returnformat="plain">
  <cfargument name="id" type="numeric" required="yes">
  <cfargument name="msg" type="string" required="yes">
  <cfset var q = "">
  <cfset var out = "">
  <cfset var lang="">

  <cfquery name="q">
    select *
    from transaction
    where transactionid=<cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.id#">
  </cfquery>
  <cfset lang = q.nativeLanguage>
  <cfif listfind("en,es,fr,it,de",lang) is 0>
    <cfset lang="en-gb">
  </cfif>
  <cfxml variable="twiml">
    <cfoutput>
     <Response>
       <Say loop="0" voice="woman" language="#lang#">
          #xmlFormat(arguments.msg)#
       </Say>
     </Response>
    </cfoutput>
 </cfxml>
 <cfreturn twiml>
</cffunction>
<!--- call the user and read from a TwiML generated script --->
<cffunction name="sendCall" access="remote" returntype="string" returnformat="plain">
   <cfargument name="id" type="numeric" required="yes">
   <cfargument name="msg" type="string" required="Yes">
   <cfset var q = "">
   <cfset var stSuccess={}>

   <cfquery name="q">
     select *
     from transaction
     where transactionid=<cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.id#">
   </cfquery>

    <cfhttp
      username="[twilio user id]"
      password="[twilio password]"
      url="https://api.twilio.com/2010-04-01/Accounts/[twilio account id]/Calls" method="post">
       <cfhttpparam name="From" value="+15045951048" type="formfield">
       <cfhttpparam name="To" value="#q.phone#" type="formfield">
       <cfhttpparam name="Url" value="http://webapps.figleaf.com/wallet/transaction.cfc?method=playMsg&id=#arguments.id#&msg=#urlencodedformat(arguments.msg)#" type="formfield">
    </cfhttp>
    <cflog file="twilio" text="#cfhttp.FileContent#">
    <cfset stSuccess= {success: true}>
    <cfreturn serializeJson(stSuccess)>
 </cffunction>

To the Victor goes the Spoils

shinySo we got the app produced and debugged to the point where we could demonstrate all of the core functionality within a frenetic 6 hour period. After our allotted two minute (!) presentation that seemingly went by in about two seconds, we sat down and waited for the results. The folks from Nokia were kind enough to award us a new phone and nice tote bag for our efforts. I was happy to add a Microsoft device to Fig Leaf’s burgeoning collection of mobile test devices.

And then I went home and passed out for four hours from exhaustion.

You can try out the app at http://webapps.figleaf.com/wallet on your mobile device or by using Google Chrome on your PC (note that the Twilio features are disabled as we’re using a test account). Not too bad for 6 hours of frantic coding!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s