Over a million developers have joined DZone.

Adding Real World Context to Mobile Apps with Beacons

· Mobile Zone

Editor's Note: Article originally written by Jen Looper for Telerik.

estimote_beacons_header

The idea of creating consumer-ready indoor proximity systems has been the dream of many startups recently. While satellite-driven GPS works brilliantly for positioning your car on a road, it doesn’t work so well for more fine-grained positioning. You might want to find a spot on a given floor of a building, a brand of soup on a shelf in a grocery store, or even one product on sale in a display. Finding a scalable technical solution that offers beaconing or “nearables” for retailers has proven challenging. ByteLight, for example, offers a solution to combine existing indoor LED light infrastructure, Bluetooth, and sensors to pinpoint customers within a store and send them alerts. Anyone who has shopped at the Stop and Shop grocery stores and used a ScanIt scanner, jumping when the thing beeps and flashes a coupon when you pass by a product realizes the power of these type of sensors. I admit to preferring to shop in a store that offers this type of scanning device to get through checkout more quickly.

There are affordable ways to leverage the power of beacons as a promotional tool, even for smaller businesses.


Beacons for the Rest of Us

For the store or non-profit that can’t afford to invest in a complete infrastructure overhaul like Stop and Shop’s, there are still ways to leverage the power of beacons. Introducing Estimotes!

Estimote beacons are small devices that are offered in two kits priced at $99 per set. These little beacons are small rock-shaped rubber devices with a sticky backing that can be attached to a product, shelf or wall. Coming soon are beacon stickers that will be even more easily attached to small areas. The prospects are very exciting for these little machines…and they are very pretty as well!

estimotes


Estimote beacons have several use cases. If you have four beacons, you can place them around a room and configure a mobile app to localize itself in relation to the beacons. This way, one app user could find another by their relationship between their device and the beacons. Since the base developer kit comes with three beacons, however, we can use them for a proximity exercise, which shows how close we are to the actual beacon using a mobile phone. There are plenty of real-world use cases for this functionality, but for demo purposes, it could be fun to create a ‘hot/cold’ game app to show how close or far your device is from the beacon. You could even create a really epic scavenger hunt for multiple users. For the purposes of this tutorial, I’m going to demonstrate how to create a more ‘civic hack’ type of project, an app for a public library.


The Plugin Marketplace to the Rescue!

Until recently, the only way an app developer could leverage Estimote beacons was writing native code, as there is no REST API at the time of this writing. Thankfully, as of late October, we have a Cordova plugin ready and waiting for us to use!

For this app, we are going to use two of the plugins from Telerik’s curated marketplace: the Estimote plugin and the barcode scanner plugin. With these two plugins, we will be able to put our beacon on a book that the library wants to feature. After finding the book, the library patron will use the barcode scanner to scan the book’s ISBN number and lookup more information about it. Let’s get started!


Create the AppBuilder Project

To start, create an new project in AppBuilder by logging into AppBuilder and clicking ‘create new project’. This can just be a blank project with a tabbed interface. With just a few files, we’re going to create an interface for a two-tab UI. The first tab is going to be where the app will search for beacons, and the second will include a button to start a barcode scanner and a link that will appear after the barcode has been scanned, for the user to discover a book. We’re going to hook the app up to a simple ISBN lookup but you could extend the app to include a more detailed ISBN search.


Create the User Interface

Add two files for the two tabs within our interface:

  • views/beacons.html
  • views/scan.html

In the beacons file, create a simple two-button layout:

<div data-role="view" data-title="Find Featured Books" data-layout="main" data-model="app.beaconService.viewModel">
  <div class="rounded">

            <div id="beaconlog">
                Looking for beacons...
            </div>

            <div class="buttons">
                <button data-role="button" class="button start" data-bind="click: start">Start Searching</button>

                <button data-role="button" class="button stop" data-bind="click: stop">Stop Searching</button>
            </div>
   </div>
</div>

In scan.html create just one button and an empty div that will house the link to the book:

<div data-role="view" data-title="Scan an ISBN bar code" data-layout="main" data-model="app.beaconService.viewModel">
   <div class="rounded">
      You found a beacon! Now scan the ISBN bar code on the back cover to learn more about the featured book.
       
        <div class="buttons">
             <button class="button scan">Scan a Bar Code</button>
        </div>
       
       <div id="bookinfo">
         
       </div>
       
   </div>
</div>

Configure index.html

In index.html, let’s add our two views to a tabstrip:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
    <link href="kendo/styles/kendo.mobile.all.min.css" rel="stylesheet" />
    <link href="styles/main.css" rel="stylesheet" />

    <script src="cordova.js"></script>
    <script src="kendo/js/jquery.min.js"></script>
    <script src="kendo/js/kendo.mobile.min.js"></script>
    <script src="scripts/beacons.js"></script>    
    <script src="scripts/app.js"></script>

</head>
<body>

    <div data-role="layout" data-id="main">
        <div data-role="header">
            <div data-role="navbar">
                <span data-role="view-title"></span>
            </div>
        </div>

        <!-- application views will be rendered here -->

        <div data-role="footer">
            <div data-role="tabstrip">
                <a href="views/beacons.html"><div class="icon icon-target"></div>Beacons</a>   
                <a href="views/scan.html"><div class="icon icon-focus"></div>Scan</a>
            </div>
        </div>
    </div>

</body>
</html>

In scripts/app.js, set beacons.html as the initial view:

app = new kendo.mobile.Application(document.body, {
    // you can change the default transition (slide, zoom or fade)
    transition: 'slide',

    // comment out the following line to get a UI which matches the look
    // and feel of the operating system
    skin: 'flat',

    // the application needs to know which view to load first
    initial: 'views/beacons.html'
});

Making the Buttons Work

Finally, let’s add a beacons.js file to the scripts folder. This file is the ‘brains’ of the app in that responds to user input and uses the plugin to gather the necessary information about beacon locations:

// if a beacon is detected, we show the data received
function onBeaconsReceived(result) {
    if (result.beacons && result.beacons.length > 0) {
        var msg = "<b>I found " + result.beacons.length + " beacons! Find them and scan the ISBN codes of the books to which they are attached to learn more about them.</b><br/>";
        for (var i=0; i<result.beacons.length; i++) {
            var beacon = result.beacons[i];
            if(beacon.distance > 0){
                msg += "<br/>";

                if (beacon.color !== undefined) {
                    msg += "There is a <b>" + beacon.color + "</b> beacon ";
                }

            msg += "within " + beacon.distance + " meters of this location.<br/>";
            }
            else{
                msg += "...but it's too far to find. Try retracing your steps to find it.<br/>";
            }

        }

    }

    else {
        var msg = "I haven't found a beacon just yet. Let's keep looking!"
    }

    document.getElementById('beaconlog').innerHTML = msg;
}

// watch for the beacon's signal
document.addEventListener('beaconsReceived', onBeaconsReceived, false);

(function (global) {
    var BeaconViewModel,
        app = global.app = global.app || {};

    BeaconViewModel = kendo.data.ObservableObject.extend({

        start: function () {
        //note, we can't test custom plugins in simulator, so this code reminds us to test on device
            if (!this.checkSimulator()) {
                window.estimote.startRanging("Beacons");
            }
        },
    //sometimes, we want the app to stop checking for beacons, which it otherwise does every second.
        stop: function () {
            if (!this.checkSimulator()) {
                window.estimote.stopRanging();
            }
        },

        //fired when the 'scan' button is pressed
        scan: function () {

            cordova.plugins.barcodeScanner.scan(

            function (result) {
                setTimeout(function() { 
                    var url = 'http://www.lookupbyisbn.com/Search/Book/' + result.text + '/1';                
                    var bookinfo = "<a href='#' onclick=window.open('" + url + "','_blank')>Learn more about this book</a>";
                    document.getElementById('bookinfo').innerHTML = bookinfo;                    
                }, 0);


            },

            function (error) {
                alert("Scanning failed: " + error);
            }
          )


        },

   //are we on sim?
   checkSimulator: function() {
            if (window.navigator.simulator === true) {
                alert('This plugin is not available in the simulator.');
                return true;
            } else if (window.estimote === undefined) {
                alert('Plugin not found. Maybe you are running in AppBuilder Companion app which currently does not support this plugin.');
                return true;
            } else {
                return false;
            }
        }

    });

    app.beaconService = {
        viewModel: new BeaconViewModel()
    };
})(window);

Plug In the Plugins

AppBuilder makes it very easy to add Cordova plugins to any project. To add a plugin, right-click on the app’s name in the right-hand Project Navigator panel and select ‘Manage Packages’.

navigator


Within the window that pops up, navigate to the Plugin Marketplace tab and select the Barcode Scanner and Estimote plugins. Click ‘install’ on each to install them.

marketplace


Make It Prettier

I have recently begun using Coolors to generate pretty color palettes for my apps, so I picked one and added some custom CSS and icons from icomoon. With just 100 lines of CSS, we get an app that looks really clean:

body, html {
    margin: 0;
    height: 100%;
    width: 100%;    
}
/*custom overrides*/
.km-view-title,.km-footer{
    background-color: #A2B03F;
}
.km-flat{
    font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 
   font-weight: 300;
}
.km-flat .km-content{
    background-color: #B7F2F6;
    color: #302B2F;
}
.km-flat .km-tabstrip .km-state-active{
    background-color: #DE4823;
}
.rounded{
    border-radius:5px;
    background-color:#FFFEF0;
    margin:10px;
    padding:10px;
}
.km-phone .km-tabstrip .km-icon{
    display:none;
}
.km-flat .km-tabstrip .km-button{
    font-size: 1em;
}

.start{
     background-color: #A2B03F !important;
     width:48%;
}
.scan{
    background-color: #A2B03F !important;
    width:100%;
    margin-top:20px !important;
    margin-bottom:20px !important;
}
.stop{
     background-color: #DE4823 !important;
     width:48%;
}
#beaconlog{
    text-align: left; 
    width:100%; 
    border: 1px solid #DDD; 
    border-radius: 5px; 
    padding: 5px;
    margin-bottom:10px;
    color: #666;
}
#bookinfo{
    text-align: left; 
    width:100%; 
    padding: 5px;
    color: #666;
    font-size:2em;
    font-weight:bold;
    text-align:center;
}
@font-face {
    font-family: 'icomoon';
    src:url('fonts/icomoon.eot?efunuv');
    src:url('fonts/icomoon.eot?#iefixefunuv') format('embedded-opentype'),
        url('fonts/icomoon.woff?efunuv') format('woff'),
        url('fonts/icomoon.ttf?efunuv') format('truetype'),
        url('fonts/icomoon.svg?efunuv#icomoon') format('svg');
    font-weight: normal;
    font-style: normal;
}

[class^="icon-"], [class*=" icon-"] {
    font-family: 'icomoon';
    speak: none;
    font-style: normal;
    font-size: 3em;
    font-weight: normal;
    font-variant: normal;
    text-transform: none;
    line-height: 1.5;

    /* Better Font Rendering =========== */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.icon-focus:before {
    content: "\e01b";
}
.icon-lightbulb:before {
    content: "\e030";
}
.icon-target:before {
    content: "\e04e";
}

Testing on a Device

Now, we need to deploy our app to an actual device to test the plugin. It’s not possible to test a custom plugin such as the Estimote or Barcode scanner on AppBuilder’s in-browser client, companion app, or the CLI – you’ll need to test on a real device. Preparing for a device build can be a somewhat involved procedure, especially for iOS where you will need to install a certificate, provision your device, and build locally, after which AppBuilder will be able to deploy your app to a connected device.

Note, the app identifier that you use for your provisioning profile needs to match the identifier that is included in the codebase. Double click ‘properties’ in the Project Navigator and edit the Application Identifier field. It should look like ‘com.company.appname’.

Please refer to the following documentation that walks you through the process on each OS. You’ll need to complete this setup for your device before continuing.

Once you have completed your setup, you can click ‘build’ and the interface will walk you through installing the app to your connected device. When the QR code is produced by AppBuilder, you can simply use a QR reader on your device to download and install the freshly-built app.


Using the App

Of course, you’ll need some Estimote beacons to actually test this app. Assuming you have one, you can take it and place it on a book in your personal library. Within the app, click the ‘start searching’ button and walk towards the book. You’ll see the distance between yourself and the beacon decrease.

screenshot_1


Once you get to the book, open the ‘Scan’ tab. Click the ‘Scan’ button and scan the barcode on the back of the book to get its ISBN.

screenshot_2

A link will appear once the scanner picks up the barcode. Click the link to navigate and look up by ISBN.

screenshot_3


Extend this demo!

The full codebase is available to you on GitHub. Please feel free fork it, tear it apart, get some beacons, try the plugins, see what you can create, and tell us about it!

Topics:

Published at DZone with permission of Burke Holland, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}