(For more resources related to this topic, see here.)
QR codes
We love our smartphones. We love showing off what our smartphones can do. So, when those cryptic squares, as shown in the following figure, started showing up all over the place and befuddling the masses, smartphone users quickly stepped up and started showing people what it’s all about in the same overly-enthusiastic manner that we whip them out to answer even the most trivial question heard in passing. And, since it looks like NFC isn’t taking off anytime soon, we’d better be familiar with QR codes and how to leverage them.
The data shows that knowledge and usage of QR codes is very high according to surveys:(http://researchaccess.com/2012/01/new-data-on-qrcode-adoption/)
- More than two-thirds of smartphone users have scanned a code
- More than 70 percent of the users say they’d do it again (especially for a discount)
Wait, what does this have to do with jQuery Mobile? Traffic. Big-time successful traffic. A banner ad is considered successful if only two percent of people lick through (http://en.wikipedia.org/wiki/Clickthrough_rate). QR codes get more than 66 percent! I’d say it’s a pretty good way to get people to our reations and, thus, should be of concern. But QR codes are for more than just URLs. Here we have a URL, a block of text, a phone number, and an SMS in the following QR codes:
There are many ways to generate QR codes (http://www.the-qrcode-generator.com/, http://www.qrstuff.com/). Really, just search for QR Code Generator on Google and you’ll have numerous options.
Let us consider a local movie theater chain. Dickinson Theatres (dtmovies.com) has been around since the 1920s and is considering throwing its hat into the mobile ring. Perhaps they will invest in a mobile website, and go all-out in placing posters and ads in bus stops and other outdoor locations. Naturally, people are going to start scanning, and this is valuable to us because they’re going to tell us exactly which locations are paying off. This is really a first in the advertising industry. We have a medium that seems to spur people to interact on devices that will tell us exactly where they were when they scanned it. Geolocation matters and this can help us find the right locations.
Geolocation
When GPS first came out on phones, it was pretty useless for anything other than police tracking in case of emergencies. Today, it is making the devices that we hold in our hands even more personal than our personal computers. For now, we can get a latitude, longitude, and timestamp very dependably. The geolocation API specification from the W3C can be found at http://dev.w3.org/geo/api/spec-source.html. For now, we’ll pretend that we have a poster prompting the user to scan a QR code to find the nearest theater and show the timings. It would bring the user to a page like this:
Since there’s no better first date than dinner and a movie, the movie going crowd tends to skew a bit to the younger side. Unfortunately, that group does not tend to have a lot of money. They may have more feature phones than smartphones. Some might only have very basic browsers. Maybe they have JavaScript, but we can’t count on it. If they do, they might have geolocation. Regardless, given the audience, progressive enhancement is going to be the key.
The first thing we’ll do is create a base level page with a simple form that will submit a zip code to a server. Since we’re using our template from before, we’ll add validation to the form for anyone who has JavaScript using the validateMe class. If they have JavaScript and geolocation, we’ll replace the form with a message saying that we’re trying to find their location. For now, don’t worry about creating this file. The source code is incomplete at this stage. This page will evolve and the final version will be in the source package for the article in the file called qrresponse. php as shown in the following code:
<?php $documentTitle = "Dickinson Theatres"; $headerLeftHref = "/"; $headerLeftLinkText = "Home"; $headerLeftIcon = "home"; $headerTitle = ""; $headerRightHref = "tel:8165555555"; $headerRightLinkText = "Call"; $headerRightIcon = "grid"; $fullSiteLinkHref = "/"; ?> <!DOCTYPE html> <html> <head> <?php include("includes/meta.php"); ?> </head> <body> <div id="qrfindclosest" data-role="page"> <div class="logoContainer ui-shadow"></div> <div data-role="content"> <div id="latLong> <form id="findTheaterForm" action="fullshowtimes.php" method="get" class="validateMe"> <p> <label for="zip">Enter Zip Code</label> <input type="tel" name="zip" id="zip" class="required number"/> </p> <p><input type="submit" value="Go"></p> </form> </div> <p> <ul id="showing" data-role="listview" class="movieListings" data-dividertheme="g"> </ul> </p> </div> <?php include("includes/footer.php"); ?> </div> <script type="text/javascript"> //We'll put our page specific code here soon </script> </body> </html>
For anyone who does not have JavaScript, this is what they will see, nothing special. We could spruce it up with a little CSS but what would be the point? If they’re on a browser that doesn’t have JavaScript, there’s pretty good chance their browser is also miserable at rendering CSS. That’s fine really. After all, progressive enhancement doesn’t necessarily mean making it wonderful for everyone, it just means being sure it works for everyone. Most will never see this but if they do, it will work just fine
For everyone else, we’ll need to start working with JavaScript to get our theater data in a format we can digest programmatically. JSON is perfectly suited for this task. If you are already familiar with the concept of JSON, skip to the next paragraph now. If you’re not familiar with it, basically, it’s another way of shipping data across the Interwebs. It’s like XML but more useful. It’s less verbose and can be directly interacted with and manipulated using JavaScript because it’s actually written in JavaScript. JSON is an acronym for JavaScript Object Notation. A special thank you goes out to Douglas Crockford (the father of JSON). XML still has its place on the server. It has no business in the browser as a data format if you can get JSON. This is such a widespread view that at the last developer conference I went to, one of the speakers chuckled as he asked, “Is anyone still actually using XML?”
{ "theaters":[ { "id":161, "name":"Chenal 9 IMAX Theatre", "address":"17825 Chenal Parkway", "city":"Little Rock", "state":"AR", "zip":"72223", "distance":9999, "geo":{"lat":34.7684775,"long":-92.4599322}, "phone":"501-821-2616" }, { "id":158, "name":"Gateway 12 IMAX Theatre", "address":"1935 S. Signal Butte", "city":"Mesa", "state":"AZ", "zip":"85209", "distance":9999, "geo":{"lat":33.3788674,"long":-111.6016081}, "phone":"480-354-8030" }, { "id":135, "name":"Northglen 14 Theatre", "address":"4900 N.E. 80th Street", "city":"Kansas City", "state":"MO", "zip":"64119", "distance":9999, "geo":{"lat":39.240027,"long":-94.5226432}, "phone":"816-468-1100" } ] }
Now that we have data to work with, we can prepare the on-page scripts. Let’s put the following chunks of JavaScript in a script tag at the bottom of the HTML where we had the comment: We’ll put our page specific code here soon
//declare our global variables var theaterData = null; var timestamp = null; var latitude = null; var longitude = null; var closestTheater = null; //Once the page is initialized, hide the manual zip code form //and place a message saying that we're attempting to find //their location. $(document).on("pageinit", "#qrfindclosest", function(){ if(navigator.geolocation){ $("#findTheaterForm").hide(); $("#latLong").append("<p id='finding'>Finding your location...</ p>"); } }); //Once the page is showing, go grab the theater data and find out which one is closest. $(document).on("pageshow", "#qrfindclosest", function(){ theaterData = $.getJSON("js/theaters.js", function(data){ theaterData = data; selectClosestTheater(); }); }); function selectClosestTheater(){ navigator.geolocation.getCurrentPosition( function(position) { //success latitude = position.coords.latitude; longitude = position.coords.longitude; timestamp = position.timestamp; for(var x = 0; x < theaterData.theaters.length; x++) { var theater = theaterData.theaters[x]; var distance = getDistance(latitude, longitude, theater.geo.lat, theater.geo.long); theaterData.theaters[x].distance = distance; }} theaterData.theaters.sort(compareDistances); closestTheater = theaterData.theaters[0]; _gaq.push(['_trackEvent', "qr", "ad_scan", (""+latitude+","+longitude) ]); var dt = new Date(); dt.setTime(timestamp); $("#latLong").html("<div class='theaterName'>" +closestTheater.name+"</div><strong>" +closestTheater.distance.toFixed(2) +"miles</strong><br/>" +closestTheater.address+"<br/>" +closestTheater.city+", "+closestTheater.state+" " +closestTheater.zip+"<br/><a href='tel:" +closestTheater.phone+"'>" +closestTheater.phone+"</a>"); $("#showing").load("showtimes.php", function(){ $("#showing").listview('refresh'); }); }, function(error){ //error switch(error.code) { case error.TIMEOUT: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Timeout</div>"); break; case error.POSITION_UNAVAILABLE: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Position unavailable</div>"); break; case error.PERMISSION_DENIED: $("#latLong").prepend("<div class='ui-bar-e'> Unable to get your position: Permission denied. You may want to check your settings.</div>"); break; case error.UNKNOWN_ERROR: $("#latLong").prepend("<div class='ui-bar-e'> Unknown error while trying to access your position.</div>"); break; } $("#finding").hide(); $("#findTheaterForm").show(); }, {maximumAge:600000}); //nothing too stale }
The key here is the function geolocation.getCurrentPosition, which will prompt the user to allow us access to their location data, as shown here on iPhone
If somebody is a privacy advocate, they may have turned off all location services. In this case, we’ll need to inform the user that their choice has impacted our ability to help them. That’s what the error function is all about. In such a case, we’ll display an error message and show the standard form again.