Uw betrouwbare partner voor (mobiele) web applicaties
TwitterEmailRSS

Phonegap camera implementation

We are currently working on the implementation of a new App for a customer using the Phonegap framework to simplify development for multiple platforms. One of the requirements the customer came up with is the possiblity for a user of the App to upload photo’s that he made with the camera of his mobile device. Is this possible using the Phonegap framework? Of course it is, and this blog will show you how.

Photo upload using the Phonegap camera object

Several steps are needed to implement the photo upload functionality:

  1. Make sure your App has access to the mobile device’s camera functionality.
  2. Get you App to open the device’s native camera dialog.
  3. Get the photo from the native camera dialog.
  4. Upload the photo to your server.

1. Access to the device’s camera functionality

It is necessary to configure in your Apps’s Phonegap configuration that you’ll be using the Phonegap camera plugin.We are using Phonegap Build, which means that a single configuration file (config.xml) can be used for both Android and iOS.

The following lines have to be added to the config.xml:

<feature name="http://api.phonegap.com/1.0/camera"/>
<feature name="http://api.phonegap.com/1.0/file"/>

Note that the second line is needed because on Android you need to configure the user permission WRITE_EXTERNAL_STORAGE, see the Phonegap API documentation. This is needed to store the picture taken with the camera on the local file system.

When you are not using Phonegap Build, this needs to be done differently for Android and iOS.

Android:

Config.xml:
<plugin name="Camera" value="org.apache.cordova.CameraLauncher" />
AndroidManifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

iOS:

Config.xml:
<plugin name="Camera" value="CDVCamera" />

2. Open the device’s native camera dialog

First, you want to tell the user that it is possible to upload a photo in the App. We added a camera icon to the page and added an onclick event to that icon. This onclick event opens a confirm dialog asking the user if he wants to 1) Make a photo and upload it or 2) Select a photo from the device’s gallery and upload it. After that, the Javascript method listed below is executed.

function onConfirm(buttonIndex) {
  if(buttonIndex == 1) { 
    // User wants to make a photo using the camera
    sourceType = Camera.PictureSourceType.CAMERA;
    captureImage();
  } else if (buttonIndex == 2) {
    // User wants to select a photo from the gallery
    sourceType = Camera.PictureSourceType.PHOTOLIBRARY;
  } else {
    // cancelled
  }
}

As you can see, this method does nothing more than setting the correct sourceType, which is a global var in the Javascript file and executing the method captureImage, which contains the magic. captureImage is listed below.

function captureImage() {
  navigator.camera.getPicture(
    onSuccess, 
    onFail,
    { quality:60,
      allowEdit:true, // ignored by Android
      destinationType:Camera.DestinationType.DATA_URL, //base64
      encodingType:Camera.EncodingType.JPEG,
      sourceType:sourceType,
      targetWidth:160,
      targetHeight:160
    }
  );
}

The captureImage method calls the Phonegap navigator.camera.getPicture method, which is the bridge to the native camera functionality of your mobile device. Some configuration settings are provided, i.e. the destinationType that tells Phonegap we want a base64 encoded image String, the encodingType to configure the image encoding type and the sourceType to tell the device that we want to make a new picture or select an existing image from the gallery. See the Phonegap API documentation for the complete list of configuration properties.

3. Get the photo from the native camera dialog

In order to obtain the photo from the native camera dialog, the onSuccess callback function listed in the captureImage method above needs to be implemented. Our implementation of the onSuccess method is listed below.

function onSuccess(imageData) {
  var theImage = "data:image/jpeg;base64,"+imageData;

  // immediately change the image in the view 
  clickedImage.src = theImage;

  // And now upload the photo to the server
}

4. Upload the photo to your server

To actually upload the photo to the server, we have extended the onSuccess callback function with an Ajax call to our server, which is shown in the listing below.

function onSuccess(imageData) {
  var theImage = "data:image/jpeg;base64,"+imageData;

  // immediately change the image in the view 
  clickedImage.src = theImage;

  $.mobile.loading('show'); // Show the spinner
  doPost(
    '/user/uploadphoto',
    {
       'personid': getUser().personID,
       'photo': imageData
    },
    function(data) {
      if (data.success) {
        alert('Saving photo succeeded');
      } else {
        alert('Saving photo failed');
      }
      $.mobile.loading('hide'); // Hide the spinner
    }
  );
}

On our server we store the base64 encoded JPEG file in our database for the user that uploaded it. The server side code is not included here.

Phonegap camera

As we described above, it is very easy to implement a photo upload mechanism in your Phonegap application using the Phonegap camera object. A few simple lines of Javascript code in your App is enough. Enjoy!

Using PhoneGap Build? You should.. if possible

Unless you need to add your own native code to your PhoneGap project, you can use the excellent PhoneGap Build service. If you do, you may also want to use our nice new app: Buildmeister, a mobile app for easily managing your PhoneGap Build apps.

Download Buildmeister for these platforms:

Android app on Google Play

Buildmeister - X-Services

PhoneGap pull to refresh implementation

We all know the very nice pull to refresh functionality that was introduced in the Twitter app for iOS. Though patented by Twitter, it has been implemented in many apps since. I was working on a PhoneGap app with a page containing a list of news items and was wondering how difficult it would be to implement the pull to refresh functionality on that page. This blog describes how to make your PhoneGap pull to refresh implementation.

PhoneGap pull to refresh

Google led me to the iScrollView JQuery Mobile plugin to use the iScroll scroller. Using this plugin it was fairly easy to implement pull to refresh. A few steps had to be taken. First, include the plugin stylesheets and javascript files in your html file.

<link rel="stylesheet" href="css/jquery.mobile.iscrollview.css" type="text/css"/>
<link rel="stylesheet" href="css/jquery.mobile.iscrollview-pull.css" type="text/css"/>
<script src="js/lib/iscroll.js"></script>
<script src="js/lib/jquery.mobile.iscrollview.js"></script>

Next, add the attribute data-iscroll to the container you want to add the functionality to. In my case, this was the content container of the page. See the first line in the code snippet below.

<div id="scroller" data-role="content" data-iscroll="">
 <div class="iscroll-pulldown">
   <span class="iscroll-pull-icon"></span>
   <span class="iscroll-pull-label"
         data-iscroll-loading-text="News is being refreshed"
         data-iscroll-pulled-text="Release to refresh the news">
     Refresh the news
   </span>
 </div>
 <div id="newsList"></div>
</div>

The <div> with class iscroll-pulldown contains the definition of the pulldown content that will be displayed when the user pulls down the container. Different data tags are available for the different states the pull down dialog can be in. data-iscroll-loading-text for the text to be displayed when the update action is executed and data-iscroll-pulled-text for the text to be displayed when the user has pulled down the container.

So, how do we implement the code to actually update the news items? We need some Javascript for that ofcourse! On the pageinit event of the page we need to bind javascript functions to the different events that have been defined by the plugin. See the code below how the javascript function onPullDown is defined for the iscroll-onpulldown event.

$('#newsPage').live("pageinit", function(event) {
  $(".iscroll-wrapper").bind( {
    iscroll_onpulldown : onPullDown
  });
});
function onPullDown(event, data) {
  //do your stuff here...
}

This is all that is needed to implement the pull to refresh functionality in your JQuery Mobile PhoneGap application. Enjoy!

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');
});

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.

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

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.