Add Indoor Location to an Android app

In this guide, we’ll learn how to build a simple app which uses beacons and Estimote Indoor Location to obtain precise (x,y) coordinates of its user inside an indoor space.

Tip: If you’d rather dive into ready-made code, check out the example apps in Estimote Cloud.

What’s ahead (aka Table of Contents)

Prerequisites

  • 1 x computer with Android Studio.
  • 1 x smartphone with Android 5.0 (or newer).
  • 1 x Estimote Account (sign up here).
  • 4 or more Estimote Location Beacons.

Map your space out

If you haven’t yet, start by mapping the space which we’ll later use for indoor positioning in our app.

With the map of the space ready and waiting for us in the cloud, let’s move on to building the app itself.

Set up a new project

First, let’s create a new Android Studio project. You can name it anything you want—we’ll use “HelloIndoor” in this case.

Go to the next page and set the minimum API level to 19. You can also specify other types of devices (like tablets), but we’ll stick to phones only in this tutorial. Now go to the final page and choose “Empty Activity”.

Go to your build.gradle file and under your dependencies add this line:

dependencies {
  compile 'com.estimote:indoorsdk:{INSERT THE CURRENT VERSION HERE}'
}

Make sure to sync your project—Gradle will download our SDK for you. Now we are ready to start creating our magical indoor experience!

Connect the app to Estimote Cloud

We’ll now connect our app with the Estimote Cloud to be able to download the map of the location that we created earlier.

First, go to Estimote Cloud, the “Apps” tab, and add a new “Your Own App”. It’ll give you the App ID and App Token, necessary for the app we’re building to be able to access the Cloud API. You will need them later to build the IndoorLocationManager object.

Fetch location from Estimote Cloud

We need a Location object (i.e., location metadata, such as the floor plan) to start indoor positioning, so this step is really important. Here’s how you download the Location from our cloud:

IndoorCloudManager cloudManager = new IndoorCloudManagerFactory().create(applicationContext, new EstimoteCloudCredentials("YOUR-APP-ID", "YOUR-APP-TOKEN"))
cloudManager.getLocation("my-kitchen", new CloudCallback<Location>() {
    @Override
    public void success(Location location) {
        // store the Location object for later,
        // you will need it to initialize the IndoorLocationManager!
        //
        // you can also pass it to IndoorLocationView to display a map:
        // indoorView = (IndoorLocationView) findViewById(R.id.indoor_view);
        // indoorView.setLocation(location);
    }

    @Override
    public void failure(EstimoteCloudException e) {
        // oops!
    }
});

As you can see, you need to include your appId and appToken in order to be able to make requests to Estimote Cloud. applicationContext is also required - you can get it from your activity using getApplicationContext(). Don’t forget to change the “my-kitchen” identifier to that of your own location. You’ll find the identifier on the list of your locations in Estimote Cloud.

Build Indoor Location Manager

Indoor Location Manager is the centerpiece of the Indoor SDK. Let’s add it to our project. You will need applicationContext, location (previously downloaded from Estimote Cloud), and credentials (appId/appToken):

Use IndoorLocationManagerBuilder to create the necessary object:

ScanningIndoorLocationManager indoorLocationManager =
        new IndoorLocationManagerBuilder(this, location, new EstimoteCloudCredentials("YOUR-APP-ID", "YOUR-APP-TOKEN"))
        .withDefaultScanner()
        .build();

Info: You can also build your Indoor Location Manager with the .withScannerInForegroundService(notification) option. You will need to provide an Android notification object, but your scanner will be running in the foreground service, which means it won’t be stopped by the system without your knowledge. This way, you can use it to scan even if the user has put your app in the background.

Start position updates

The easiest way is to use the ScanningIndoorLocationManager, which scans for beacons by itself. In this case, you only need to set up the position update listeners, like this:

indoorLocationManager.setOnPositionUpdateListener(new OnPositionUpdateListener() {
    @Override
    public void onPositionUpdate(LocationPosition position) {
        // here, we update the IndoorLocationView with the current position,
        // but you can use the position for anything you want
        indoorView.updatePosition(position);
    }

    @Override
    public void onPositionOutsideLocation() {
        indoorView.hidePosition();
    }
});

Now, just some start and stop code:

@Override
protected void onStart() {
    super.onStart();
    indoorLocationManager.startPositioning();
}

@Override
protected void onStop() {
    super.onStop();
    indoorLocationManager.stopPositioning();
}

Info: You can also start positioning in your activity’s onCreate() and stop it in onDestroy(). This will allow the scanning process to work when the user has stopped your activity (clicked home button etc.)

Boom, that’s all! You should now be able to get the coordinates directly to your OnPositionUpdateListener!

Next steps

You can use the coordinates obtained from Indoor Location to:

  • Trigger actions in certain parts of the space, e.g.:

     LocationPosition coffeeMachine = new LocationPosition(3.1, 7.2, 0.0);
        if(LocationExtensionsKt.distanceTo(position, coffeeMachine) > 5){
            // Start brewing coffee
        }
    
  • Store the coordinates or send them to an external server for further analysis, e.g., to create a heatmap.

  • Draw a map of the space and mark user’s position with an avatar.

    You can do that with the IndoorLocationView. Add it to the XML layout file of your Activity:

    <com.estimote.indoorsdk.view.IndoorLocationView
        android:id="@+id/indoor_view"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:background="COLOR HERE/>
    

    … then you just need to bind this view in your code - you need to pass the previously fetched location object for the view to render it.

    IndoorLocationView indoorView =
            (IndoorLocationView) findViewById(R.id.indoor_view);
    indoorView.setLocation(location);
    

Also, feel free to study the Android Indoor SDK reference to learn more about the methods exposed by the SDK.