A little over a month ago I wrote a post on Reading QR Codes in Salesforce1. At the time I added an entry to my todo list to investigate barcode reading through a similar mechanism. A twitter post from @cherfeldman asking if this was possible in Salesforce1 moved it up to the top and so I started looking into it in earnest.
One of my key requirements was to be able to process the code on device, as I didn’t want to be sending high resolution images from a phone or tablet back to the server, especially if I’m using what passes for mobile broadband outside of a big city in the UK.
Some goggling threw up a few JavaScript packages, but based on the demos provided by most of them, Eddie Larsson’s Barcode Reader looked to be the pick of the bunch. The demo page shows some pretty impressive decoding - multiple codes per image, blurry images, rotated through 90 + 180 degrees etc. It also handles a number of formats - Code128, Code93, Code39 and EAN-13.
Creating a prototype in Visualforce was exceptionally straightforward - I just had to download the JavaScript that does the heavy lifting (DecoderWorker.js) and upload this as a static resource to my developer edition of Salesforce and then cut and paste the source of the example upload page and surround it with a few Visualforce tags.
While it was great to get this working so easily, it wasn’t a huge amount of use as it stood, as it simply decodes the image and outputs the associated number. The first thing I did was to add a field to my account sobject of “Barcode”, so that scanning a barcode could take the user to a Salesforce record. I then tweaked the JavaScript to:
- Output the time taken to decode
- Allow the user to choose when to decode the image, in case a picture didn’t turn out that great
- Add a “Restart” button to reload the page, as hitting the back button and then trying to decode didn’t work 100% of the time
- Execute a controller method via JavaScript remoting to retrieve the id of the matching record
Trying this out on my laptop decoded the barcode in around 3-4 seconds, however the same thing on my iPhone or iPad took some time and the user had no idea what was happening, so clearly something to hold their interest was required.
Looking at the underlying JavaScript, it makes use of HTML5 Web Workers to fire off multiple decoders to attack the barcode from various angles concurrently.
var DecodeWorker = new Worker("{!URLFOR($Resource.DecodeWorker)}");
var RightWorker = new Worker("{!URLFOR($Resource.DecodeWorker)}");
var LeftWorker = new Worker("{!URLFOR($Resource.DecodeWorker)}");
var FlipWorker = new Worker("{!URLFOR($Resource.DecodeWorker)}");
where $Resource.DecodeWorker is the static resource reference for the DecodeWorker.js file.
Without web workers, things would be even slower as one decoder would have to finish before the next one could start, but I was still seeing decode times of around a minute. I therefore decided to notify the user that the workers had been launched, and then post regular messages to indicate they were still running until they had either all completed or the barcode had been successfully processed. I could do this through alerts or dialogs, but a few weeks ago Jeff Douglas’ Force.com Weekly linked to a rather nice JavaScript library for notifying users - alertify.js. I’ve used this a couple of times and its reliable, fast and looks great so I decided this was the way to go.
Adding alertify to a page is as simple as including three CSS and JavaScript files - I’ve uploaded the alertify zip as a static resource in my developer edition so I can include the files from that:
<apex:stylesheet value="{!URLFOR($Resource.alertify,
'alertify.js-0.3.11/themes/alertify.core.css')}"/>
<apex:stylesheet value="{!URLFOR($Resource.alertify,
'alertify.js-0.3.11/themes/alertify.default.css')}"/>
<apex:includeScript value="{!URLFOR($Resource.alertify,
'alertify.js-0.3.11/lib/alertify.min.js')}"/>
I can then post a message to the user from JavaScript via a single function call:
alertify.log('Launching workers');
The user experience is now take a picture of a barcode or pick one from the device gallery, and click the “Decode” button if it looks good, as which point the user is told that work has started:
while the workers are running, regular messages are alerted through a JavaScript timer:
interval=setInterval(function(){alertify.log('Still working');},5000);
Then when the decoding completes, messages are displayed to indicate the success/failure and assuming success, the controller method is executed to find a record where the Barcode field matches the decoded value.
the decoded value and time taken is then displayed, and a button to navigate to the record is displayed:
Clicking the "Go to record” button goes to the record matching the barcode, which is my BrightGen account:
The full page source is available at this gist, and the associated controller at this gist. You’ll also need to set up the static resources for:
- alertify - download the zip file, then upload as a static resource named ‘alertify'
- decoder worker - download the file, then upload as a static resource named ‘DecodeWorker'.
Note that this isn't coming from the github repository, as the code has changed since I originally wrote this post - instead this is the version that I downloaded back in 2014.
Note that this won’t work on Android 4.4, as it appears that the ability to access the camera via a file input was removed from the chromium web view in this version - you can read more about this in the Cordova JIRA entry.