Uw betrouwbare partner voor (mobiele) web applicaties
TwitterEmailRSS

Implementing JQuery Mobile buttons without unexpected side effects

Recently we started the implementation of a new mobile application. We are using the Phonegap / Apache Cordova framework to have a single code base for multiple target platforms. The code base exists of HTML, CSS and Javascript. We use JQuery Mobile to implement the HTML pages.

JQuery Mobile offers different templates, the single page template and the multi page template. We are using the multi page template, which of course means that there are multiple pages in our application. These pages are defined in a single HTML file using the following JQuery Mobile code:

<div id="pageOne" data-role="page">
  Page code here
</div>
<div id="pageTwo" data-role="page">
  Page code here
</div>

Adding behaviour to your buttons

After creating the pages, we added some buttons to these pages. To add behavior to these buttons, we used the JQuery “pagebeforeshow” event of the page.

$('#pageOne').live('pagebeforeshow', function(e, data) {
  $('#myButton').bind('click', function() {
    $.mobile.changePage("#pageTwo", {transition: 'slideup'});
  });
});

After creating several pages and buttons we were ready to run our application to see if the navigation between the pages worked correctly. At first, we were very happy because all buttons seemed to work correctly. However, clicking around for a bit resulted in some very strange unexpected behavior. It turned out that each time the page was shown, the event handler was added to the page. Multiple event handlers to buttons result in ‘clickthroughs’, which basically means that page navigation is messed up.

How to fix this?

Finding the root cause proved to be more difficult than fixing the issue. Just unbind the click behavior of buttons when hiding the page, see the code snippet below. This will prevent multiple event handlers being added to your buttons.

$('#personPage').live('pagebeforehide', function(e, data) {
  $('#previousPerson').unbind('click');
  $('#nextPerson').unbind('click');
});

Game TV

I’ve been wanting to create a mobile videogame-focused app for a looooooooong time. During the Christmas break I finally has some spare time and created…: Game TV  😛 😮 🙂   sorry, can’t stop smiling

Game TV.. wut?

So what is it? By installing Game TV the user will always know which channel (game related websites or youtube channels, to be precise) has added new content. By clicking the channel, the user will see a list of recently added video’s and is able to play the video and share it with friends (email) or the world (twitter). Game TV doesn’t bother you with registration or advertisements, just pick and play!

Game TV at a glance

Technique

Well, we’re all techies here, right? So let’s highlight some technical bits.

Client

 PhoneGap / Cordova wrapped JQuery Mobile app (css3, html5, js). Kindly built by PhoneGap build. Notable stuff:

– Responsive design with CSS3 is cool. See the upper left screenshot in the pic above. This is how I position the video (block a) next to the description (block b)  when in landscape, or above the description when in portrait orientation (default JQM width of these blocks is 50%):

@media all and (orientation:portrait) {
  #videoplayer .ui-block-a, #videoplayer .ui-block-b {
    width: 100%;
  }
}

– As the app will *cough* obviously hit 100.000+ users shortly, clientside caching is very important. We cache the channels and videos in sessionstorage. But to guarantee a quick startup, we also leverage localstorage. When the app is started, the content is immediately retrieved from localstorage and asynchronously updated from the server, showing the diff to the user when done (red  count bubbles, we all know them).

– Since I want my app to be present in all appstores, I had to localise some bits of text. Keith Wood has created a simple yet very effective jquery localisation library.

Server

Play! Framework Java app, doing two things:

– Harvesting new content for all channels. 1 Job per channel.

– Serving the harvested content as JSON to the client.

What I’ve learned

– Android and iOS have great native videoplayers, as long as you invoke them correctly.

– Android animations are still not as smooth as on iOS. Fortunately, JQuery Mobile allows you to easily configure them per platform. I used this snippet:

// override some defaults, before jqm.js is loaded
$(document).bind("mobileinit", function(){
  $.mobile.defaultPageTransition = isAndroid() ? "fade" : "slide";
});

– Adding a Tweet button with a count bubble (see the upper left screenshot in the image above) is not as trivial as you’d think when you don’t want users to leave your app while tweeting. A combination of clickjacking, Twitter web intents and the PhoneGap Childbrowser plugin did the job.

– Some sites make life for a screenscraper really hard. Try to find the URL of the videos being played on this site for example. Horror!

– .. but YouTube rocks, thank you for providing easily parseable RSS feeds like this one (Gamespot channel).

– JQuery Mobile is great, but has some nasty caveats (stacking click handlers, document ready, reloading a page, etc). We’ll definitely blog about some high and lows of JQM in the future, but for now: download the Android app please please please 🙂There will probably be no iOS version because of the strict Apple review guidelines regarding streaming ‘protected’ content.

Dynamically load phonegap.js per mobile platform

Recently I was wondering how to dynamically load phonegap.js per mobile platform. I mean, when using PhoneGap Build, you could just add

<script src="phonegap.js"></script>

to the <head/> of your page, but what about running your app outside PhoneGap Build in a Simulator, on your device, or even as a website locally on your laptop?

Dynamically load phonegap.js per mobile platform

Ofcourse there are more ways to solve the problem, but what we did was really easy and working extremely well: we load our own phonegap/cordova.js file based on the useragent while loading the page and don’t include it in the head at all. Your app supports all deployment scenario’s, even on PhoneGap Build.

The code: an easy bit of JavaScript

Add this to any .js file every page of your webapp needs Cordova functionality for. We’ve included it in a core.js file, which is included in the head of every page. Note that this app doesn’t use jQuery, but Vanilla.js, the fastest JS framework on the planet.

(function loadCordova() {
  var CORDOVA_VERSION = "2.1.0";

  var CORDOVA_EDITION = null;
  if (isAndroid()) {
    CORDOVA_EDITION = "android"
  } else if (isIOS()) {
    CORDOVA_EDITION = "ios"
  }

  if (CORDOVA_EDITION != null) {
    document.write('<script type="text/javascript" src="' + 'js/lib/cordova-' + CORDOVA_VERSION + '-' + CORDOVA_EDITION + '.js"></script>');
    // now add plugins
    if (isIOS()) {
      document.write('<script type="text/javascript" src="js/lib/keychain.plugin-ios.js"></script>');
      document.write('<script type="text/javascript" src="js/lib/certificatepinner.plugin-ios.js"></script>');
    }
  }
})();

Let me explain the highlighted lines:

Lines 5 & 7: Determine the platform by querying the useragent, nothing fancy, so didn’t include details.

Line 12: The correct version for your platform is loaded. By using document.write the library gets evaluated immediately and that’s what we want!

Line 19: Don’t wait for ‘document ready’ or similar events, we want the js evaluated right away.

Cordova LocalStorage cleared after first App launch on iOS 6

“OMG WTF ! ? “

That’s what we thought today when we were hunting a bug in a PhoneGap / Cordova iOS app.

Symptoms

Upon first launch of the app (in the simulator, from PhoneGap build, or the AppStore) the app saves registration info in LocalStorage. Next time the app launches, it checks LS and recognizes you as a known user.

However, upon the second launch, we had to register again and found out that the LS was empty!

The Problem: Cordova LocalStorage cleared

Googling around, we found it’s actually a known issue on iOS 6 in Cordova 2.1.0: Upon first launch, Cordova tells iOS to remember anything the app stores in LS. This is done with a flag. However, the flag is set too late, so anything put in LS on the first run is not saved to the correct location.

The next time the app is started (and any consecutive time for that matter), the flag has already been set and the LS will behave as you’d expect (surviving app restarts, crashes, app upgrades, etc).

The solution

With this blog we hope to save some fellow coders a few hours of bug hunting. The suggestion done by Christian Hemker in the issue mentioned previously is the right thing to do. We had to roll out a quick fix, so there was no time to upgrade to a newer version of Cordova; we had to patch Cordova 2.1.0 (this bugfix was released with Cordova 2.2.0).

Don’t forget to do a Product > Clean in XCode, so the Cordova files get recompiled along with your project upon the next build.

Using PhoneGap Build?

Unfortunately, it’s not currently possible to add your custom Java code to apps built with PhoneGap Build. But if you do use that great Adobe cloudservice, check out this nice new app we created: Buildmeister, a mobile app for managing your PhoneGap Build apps.

Download Buildmeister for these platforms:

Android app on Google Play

Buildmeister - X-Services

 

Control soft keyboard on Android 2.2 device in PhoneGap / Cordova application

Currently we are developing an application using the PhoneGap / Apache Cordova framework. During the project it became clear that it is difficult to control the behavior of the soft keyboard on Android devices. On iOS the keyboard works exactly as expected but on an Android 2.2 device (HTC Desire) we face some unexpected behavior.

The problem

We have a screen with a form with several input fields on it. After entering the value in the first input field, we want to step to the next input field using the soft keyboard enter button (Note that the Android 2.2 keyboard does not have a previous and next button) and the keyboard should keep popped up to enter the value in the second input field. This turned out to be difficult to implement. See below our solution.

The first implementation

In PhoneGap / Apache Cordova we can use Javascript to handle the enter event and set the focus on the next input field (see code below).

  x$('#inputfield1').on('keyup', function(e) {
    var theEvent = e || window.event;
    var keyPressed = theEvent.keyCode || theEvent.which;
    if (keyPressed == 13) {
    	// On enter, go to next input field
        document.getElementById('inputfield2').focus();
    }
	return true;
  });

So far, so good. The code seems clean and simple and it works! On our iPhone 4s the focus is set on the next input field and the keyboard is available to enter. On our Samsung Galaxy s3 we cannot use the enter button on the keyboard, but we have a previous and next button to navigate between the input fields, so a workaround is available. On our HTC Desire Android 2.2 device however, we don’t have a previous and next button available on the soft keyboard. The enter button sets the focus on the next input field, but the soft keyboard disappears. Now that is not helping users to enter their input quickly!

Phonegap / Cordova plugin

A quick google search on “android soft keyboard phonegap show hide problem” gave us 111.000 hits. One of the first hits led us to the PhoneGap SoftKeyBoard plugin. This is a Java PhoneGap plugin for Android devices to show and hide the soft keyboard. The implementation of the method showKeyboard looks like this:

public void showKeyBoard() {
  InputMethodManager mgr =
    (InputMethodManager) this.ctx.getSystemService(
    Context.INPUT_METHOD_SERVICE);

  mgr.showSoftInput(webView,
    InputMethodManager.SHOW_IMPLICIT);

  ((InputMethodManager) this.ctx.getSystemService(
    Context.INPUT_METHOD_SERVICE))
    .showSoftInput(webView, 0);
}

Unfortunately, using the plugin did not solve our problem. It lead us to another idea however. We changed the implementation of the method showKeyBoard to:

public void showKeyBoard() {
  InputMethodManager mgr = (InputMethodManager)
    this.ctx.getSystemService(
    Context.INPUT_METHOD_SERVICE);

  if (!isKeyBoardShowing()) {
    mgr.toggleSoftInput(0, 0);
  }
}

Using this implementation we are able to show and hide the soft keyboard using the javascript below.

x$('#inputfield1').on('keyup', function(e) {
  var theEvent = e || window.event;
  var keyPressed = theEvent.keyCode || theEvent.which;
  if (keyPressed == 13) {
    // On enter, go to next input field
    document.getElementById('inputfield2').focus();
    showSoftKeyBoard();
  }
  return true;
});

function showSoftKeyBoard() {
  if (isAndroid()) {
    window.plugins.SoftKeyBoard.show(function () {
      //success
      console.log("Showing keyboard succeeded");
    },function () {
       //fail
       console.log("Showing keyboard failed");
    });
  }
}

So, slightly adjusting the java code of the Android SoftKeyBoard plugin solved the problem for our HTC Desire Android 2.2 device.

Using PhoneGap Build?

Unfortunately, it’s not currently possible to add your custom Java code to apps built with PhoneGap Build. But if you do use that great Adobe cloudservice, check out this nice new app we created: Buildmeister, a mobile app for managing your PhoneGap Build apps.

Download Buildmeister for these platforms:

Android app on Google Play

Buildmeister - X-Services

Hide iOS keyboard Form navigation buttons in PhoneGap / Cordova

iOS HTML keyboard with navigation bar

The default keyboard

You have probably seen this keyboard before: the default iOS keyboard that pops up for <input type=”tel”> form fields.

I don’t want those buttons!

The numeric keys are just fine, but the Previous/Next/Done buttons are not what I want when showing a page with only one input field. So… I wondered if the navigational buttons could be removed in my PhoneGap / Cordova HTML5 app.
iOS HTML keyboard without navigation bar

A solution 🙂

Fortunately, the internet is full of people who have similar problems, so a quick look around the globe revealed this great post.

UPDATE July 2013: simply adding this to your config.xml may get the trick done as well. BEWARE: it’s an app-wide setting, so if you need more fine grain control, read on. <preference name=”HideKeyboardFormAccessoryBar” value=”true”/>

But wait, there’s more!

This blog entry wouldn’t have existed if that was it. What I actually wanted, was an on demand solution; for one page show the buttonbar, and hide it on other pages.

Conditionally show or hide the navbar

What I wanted was controlling the appearance of the navigation bar based on a tag in the HTML. A custom class for the body tag seemed right, so I added this to the page for which the navigation bar should be hidden:

<body class="hideKeyboardNavBar">

So all I had to do was adjusting the piece of obj-c I added to my PhoneGap wrapper by checking for existence of the aforementioned classname:

NSString *hideForClass = @"hideKeyboardNavBar";

- (void)keyboardWillShow:(NSNotification*) notification {
  NSString *formClassName = [self.webView stringByEvaluatingJavaScriptFromString:@"document.body.className"];

  if ([formClassName isEqualToString:hideForClass]) {
    // remove the bar in the next runloop (not actually created at this point)
    [self performSelector:@selector(removeBar) withObject:nil afterDelay:0];
  }
}

The added code is on the highlighted lines: simply check whether or not the className of the body is equal to the className we hide the navigation bar for.

Question for the reader

I’m afraid the trick in the blogpost is as far as we can stretch the boundaries of the HTML keyboard on iOS devices. But… if anyone knows how to add a comma in the empty field below the 7, you’d be king.

Prevent your Heroku Play! apps from idling

Disclaimer: we understand that the following may be a teeny weeny bit disrespectful to the free service Heroku provides, but asking $35/month is a bit steep for a website with less than 24 requests per day. If it gets popular some day, we will ofcourse reconsider our strategy.

While looking for a cloudservice to host our libwat.ch Play! (Java) app, we stumbled upon Heroku.com. It’s a really nice service which supports all kinds of (free and paid) configurations.

Cheap Dutch basterds as we are, we wanted the free service -so that means 1 webworker which falls asleep after an hour of inactivity. The app isn’t that popular yet (c’mon, sign up!), so it dozes off very frequently.

This means the request (person) that awakes the app must wait some 20 seconds while the app steps out of bed, brushes its teeth and combs its hair… not good!

Fortunately, only the webworker is put to sleep, so any background jobs are never idled, which leaves us with a nice opportunity to insomnatise the webworker 🙂

Just add this job to your Play app* :

@Every("55min")
public class KeepSiteAwakeJob extends Job {

  @Override
  public void doJob() throws IOException {
    new URL("http://libwat.ch").getContent();
  }

}

*) Note that if you’re not using Play!, you could still use one of the free scheduler plugins Heroku offers and have that invoke the website every sub-60 minutes.

Keeping your libraries up to date with libwat.ch

Every software developer recognizes this: You develop your application using a lot of different frameworks and libraries (JQuery, HibernateRichFaces, etc) and every now and then you have to check the website of all these frameworks and libraries to see if a new version has become available. This takes a lot of valuable time that could also be spent developing that cool new feature your client has asked for.

Here at X-Services we experienced the same issue, which made us decide to implement a new service: libwat.ch.

Libwat.ch

Libwat.ch is a (mobile) web application that offers a simple service, it notifies you of updates of your favorite frameworks and libraries. There are two ways you can be notified:

  1. Follow the @libwat_ch Twitter account. We will send a tweet when a new version of a library is available.
  2. Register at www.libwat.ch and toggle the libraries you want to be notified of when a new version is available. See the screenshot below.

Add your own libraries

Is your favorite coding library not listed on libwat.ch? Don’t worry, you can send your suggestions and we’ll add them to the list. Please give us some time to add them, as we need to implement the parsing algorithm to check if a new version has been released.

Curious? Check www.libwat.ch

Keeping your PhoneGap / Cordova app in sync with your RESTful backend

These days I’m working on a PhoneGap Apache Callback Apache Cordova app. I’m having tons of fun, and thought I’d share an issue we encountered and the lightweight but effective solution we came up with.

Cordova: speeding up your app

Cordova apps are distributed via the Application store of your mobile device. This means the static content (HTML, JS, CSS) is stored on the mobile device. This means a huge performance gain over typical desktop-targeted websites which render all HTML serverside. Even if you don’t need the awesome Javascript-to-hardware-feature bridge Cordova provides you with, you can still use it to distribute your frontend to your clients. By the way: if you don’t need the hardware features (camera, contacts, gps, etc) then don’t include cordova.js, for it saves your clientbrowsers loading a 125KB library.

You need dynamic content, right?

What’s a (web)app with only static screens? Likely not a very popular one. So we need to get (preferably JSON formatted) data from some backend. Your Javascript controller interprets this modal and updates the view accordingly.

The client doesn’t bother which backend architecture it connects to, but just to let you know, the project I mentioned uses an implementation of a JAX-WS REST API which indeed returns nicely JSON formatted strings to the client.

Problem: keeping your PhoneGap / Cordova app in sync with your RESTful backend

Now consider the following scenario: you have 10.000 users with version 1.0 to 1.3 of your awesome application. You completely rewrite the client and want to force all users to update to version 2.0. They should not use 1.x anymore, because you had to change most of your backend as well.

How would we do that, as we need the user to update the app via the AppStore/Google Play/etc.

The solution: clientside version.txt, serverside check ‘minimal required version’

What we did (and it’s no rocket science and most certainly not something lots of others have not already done in some similar way) is bundling a version.txt file with the app which contains a single line, reading ‘1.0’ or ‘4’. The version.txt can be assigned to a JS variable with a simple synchronous $.ajax call.

On the serverside we have a REST service ‘http://<server>/services/versioncheck’ which we pass the client’s version.txt when the app is started. The service returns false in case the clientversion is lower than the ‘minimal required version’ we’ve defined in the backend.

So, now that we’ve updated our app to version 2.0, the service returns false for any not yet upgraded clients and the Javascript callback will urge the user to update the app.

Asynchronous jobs with Play Framework

Ever needed to run some standalone asynchronous job from Java and found it to be a bit tedious? Read on..

Declaring a Job in Play that runs every 3 seconds  and increments a property is as simple as this:

@Every("3s")
public class PublishJob extends Job {

  private int i;

  @Override
  public void doJob() throws Exception {
    i++;
  }
}

Q: W00t! It can’t be that simple!
A: Sorry, it really is.