The Android SDK Guide

The Meridian Android SDK has all the tools you'll need to embed the Meridian Editor's maps, turn-by-turn navigation, indoor location awareness, and notifications into your own custom Android app.

Once you've added maps, routes, placemarks, and campaigns to the Meridian Editor, you can use the Meridian SDK to integrate that content into your Meridian-powered Android app.

Go here for the Meridian Android SDK Reference documentation.

Click here to download the Android SDK.

Add the SDK to Your Project

In order to simplify using the Meridian SDK library with your Android project, we've bundled our SDK code into a binary distribution of the Android Library Project (AAR) file.

Add the AAR Relative Path

Complete these steps to add the AAR file to your Android project.

  1. Choose a location for meridian-x.y.z.aar.

  2. Edit your app's build.gradle file to add the relative path to the meridian-x-y-z.aar file location inside flatDir { }, where our app will look for dependencies. Into repositories { }, insert:

    repositories { mavenCentral() // optional, probably exists in your project already

    flatDir {
        dirs '[relative file path to the AAR directory]'
    }
    

    }

Add Dependencies

To use the Meridian SDK classes in your project, add the compile information for the dependencies to the build.gradle file. These are the meridian-x.y.z.aar file and two required external dependencies.

In the build.gradle file inside dependencies { }, insert compile for the meridian-x.y.z.aar file and the two required external dependencies:

dependencies {
    compile 'com.arubanetworks.meridian:meridian:x.y.z@aar'

    // The SDK also has 2 external dependencies that will need to be included:
    compile 'com.mcxiaoke.volley:library:1.0.9@aar'
    compile 'com.squareup:otto:1.3.5'

    // To enable analytics reporting for your SDK app, include this dependency:
    compile 'com.google.android.gms:play-services-analytics:10.2.4'
    // After calling `Meridian.configure()`, call `MeridianAnalytics.setAppId(appId)` to start reporting stats for your location.
}

To finish enabling Google Analytics for your project, add the following to the built.gradle file:

android {
    // ...
    defaultConfig {
        // ...
        buildConfigField "String", "ANALYTICS_ID", "TRACKING_ID
        }
    }

Replace TRACKING_ID with your unique Google Analytics tracking ID.

When this is done, you'll be able to import the com.arubanetworks.meridian packages to your project and use Meridian classes in your source files.

The SDK's MeridianSamples/app project folder has an example of what the finished build.gradle file should look like.

Add Permissions

To enable the Meridian SDK's location-awareness features, add the following permissions to your project's AndroidManifest.xml file:

<!-- For using GPS and Location in general -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<!-- For using location derived from bluetooth beacons -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

Configure the SDK

Before you can use the features of the Meridian SDK, you'll need to create an instance of MeridianConfig. This will configure the SDK.

Put the following line in the onCreate method of your Application class or main activity.

// Configure Meridian
Meridian.configure(this);

Using Editor Keys

Use instances of EditorKey to specify apps, maps, and placemarks that have been created in the Meridian Editor. Most of the Meridian SDK classes require a valid key during initialization.

// Creating a key representing your app in the Meridian Editor
EditorKey appKey = new EditorKey("5085468668573440");

// Creating a key representing a map in the Meridian Editor
EditorKey mapKey = EditorKey.forMap("5708115733494264", appKey.getId());

// Creating a key representing a placemark
EditorKey placemarkKey = EditorKey.forPlacemark("375634485771", mapKey);

When you create an EditorKey to represent a map, the app identifier you provide becomes the parent of the map key. Likewise, when you create a placemarkKey, the map you specify becomes the parent of that placemark.

    EditorKey placemarkKey = EditorKey.forPlacemark("375634485771", someMapKey);

    EditorKey mapKey = placemarkKey.getParent();
    Log.i(TAG, "My map ID: " + mapKey.getId());

    EditorKey appKey = mapKey.getParent();
    Log.i(TAG, "My app ID: " + appKey.getId());

Display Maps

You can use MapFragment to provide a self-contained interface for maps and directions. Simply initialize the fragment with a valid EditorKey:

EditorKey appKey = new EditorKey("5085468668573440");
MapFragment fragment = MapFragment.newInstance(appKey, null);

MapFragment hosts a MapView and implements the MapView.MapViewListener interface, handling the basic tasks delegated by MapView.

You can also use MapView directly in your own activity or fragment, optionally implementing methods defined by the MapView.MapViewListener interface so that your activity can respond to MapView events.

Call the setMapKey() method to choose the map to display. Otherwise, the default map will be loaded.

NOTE: We strongly advise using MapFragment whenever possible. If you decide to use a MapView independently, you must always pass your activity's pause/resume events to the MapView to ensure resources are managed properly.

// Using pause/resume events with a MapView in your activity

private MapView mapView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    EditorKey appKey = new EditorKey("5085468668573440");
    mapView = new MapView(this);

    // If you want to handle MapView events
    mapView.setListener(this);

    // If you want to load a map other than the default one
    mapView.setMapKey(EditorKey.forMap("5708115733494264", appKey.getId()));

    // Assume myLayout is this activity's root view
    myLayout.addView(mapView);
}

@Override
protected void onResume() {
    super.onResume();

    // MapView needs to know when its host activity is resumed
    mapView.onResume();
}

@Override
protected void onPause() {
    super.onPause();

    // MapView needs to know when its host activity is paused
    mapView.onPause();
}

Load Directions

MapFragment handles loading and presenting directions in response to user interaction. When you create the map fragment, include the pendingDestination parameter to present directions to a placemark when direction-finding resumes.

If you're using MapView directly in your activity, you'll need to load the directions manually with the Directions class.

To do so, first ensure that your activity implements the Directions.DirectionsRequestListener interface so that it can handle the results of the directions request. Use a Directions.Builder instance to configure the details of the desired route. To do this, specify the start and end points for the route using instances of DirectionsSource and DirectionsDestination, which contain information about a point or placemark on a map.

Next, call build() to create the Directions object from the builder.

Finally, call calculate() to start the directions process.

The Directions object will call onDirectionsComplete() after successful completion or onDirectionsError() if something went wrong.

In the following code example, MeridianLocation is the source of the directions.

A new route is calculated when the user's blue dot location is approximately 10 meters from the route. To customize the reroute distance, in build.gradle set MERIDIAN_DEFAULT_ROUTE_SNAP to half the desired rerouting distance.

For example, if you want the rerouting to start at 20 meters, set the MERIDIAN_DEFAULT_ROUTE_SNAP value to 10.

You can also use Meridian.getShared().setRouteSnapDistance() to set this value.

// In your Activity

private static final EditorKey appKey = new EditorKey("YOUR_APP_KEY");

private void getDirections(MeridianLocation source, Placemark destination) {
    Directions.Builder directionsBuilder = new Directions.Builder()
            .setAppKey(appKey)
            .setSource(DirectionsSource.forMapPoint(source.getMap(), source.getPoint()))
            .setDestination(DirectionsDestination.forPlacemarkKey(destination.getKey()))
            .setTransportType(TransportType.WALKING)
            .setListener(this);

    Directions directions = directionsBuilder.build();
    directions.calculate();
}

@Override
public void onDirectionsComplete(DirectionsResponse response) {
    if (response.getRoutes().size() > 0) {

        // Normally only one route will be returned
        Route route = response.getRoutes().get(0);

        // If we have a MapView we can tell it to display this route
        myMapView.setRoute(route);
    }
}

If a current location isn't available, you may want to prompt the user to choose a starting location with SearchActivity.

In the example, SearchActivity and startActivityForResult() will prompt the user to choose a starting placemark.

// In your Activity

private Directions.Builder directionsBuilder;
private static final EditorKey appKey = new EditorKey("5085468668573440");
public static final int PLACEMARK_PICKER_CODE = 42;

private void getDirections(Placemark placemark) {
    directionsBuilder = new Directions.Builder()
        .setContext(this)
        .setAppKey(appKey)
        .setDestination(DirectionsDestination.forPlacemark(placemark))
        .setTransportType(TransportType.WALKING)
        .setListener(this);

    // start a search Activity to get the starting point for the route
    startActivityForResult(SearchActivity.createIntent(this, appKey), PLACEMARK_PICKER_CODE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

    // Now that the user picked a placemark to start from, we can calculate the route
    if (requestCode == PLACEMARK_PICKER_CODE) {
        if (resultCode == Activity.RESULT_OK) {

            LocalSearchResult result = (LocalSearchResult) data.getSerializableExtra(SearchActivity.SEARCH_RESULT_KEY);
            Placemark source = result.getPlacemark();
            directionsBuilder.setSource(DirectionsSource.forPlacemark(source));
            directions = directionsBuilder.build();
            directions.calculate();
        }
    }
}

Send More Variables to Campaign Custom Endpoints

When using Campaign custom endpoints, you may want to send additional data to the custom endpoint.

To do this, extend CampaignBroadcastReceiver to create a campaign receiver.

Then override the getUserInfoForCampaign method to add custom Campaign values.

/// Extend CampaignBroadcastReceiver
public class CampaignReceiver extends  CampaignBroadcastReceiver {

    @Override
    protected void onReceive(Context context, Intent intent, String title, String message) {

        Intent notificationIntent = new Intent(context, MainActivity.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
        builder.setContentTitle(title);
        builder.setContentText(message);
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setDefaults(Notification.DEFAULT_ALL);
        builder.setContentIntent(contentIntent);
        builder.setAutoCancel(true);
        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify("com.arubanetworks.meridiansamples.CampaignReceiver".hashCode(), builder.build());
    }

/// Define custom values being sent to the custom Campaign endpoint.
    @Override
    protected Map<String, String> getUserInfoForCampaign(Context context, String campaignIdentifier) {
        HashMap<String, String> map = new HashMap<>();
        map.put("UserKey1", "userData1");
        map.put("UserKey2", "userData2");
        map.put("UserKey3", "userData3");
        return map;
    }

}

Get a User's Location

A user's most recent location can be retrieved with the LocationRequest class. This request is asynchronous.

private LocationRequest request = null;
    private static final EditorKey appKey = new EditorKey("5468665088573440");
    @Override
    public void onResume() {
        super.onResume();
        request = LocationRequest.requestCurrentLocation(getActivity(), appKey, new LocationRequest.LocationRequestListener() {
            @Override
            public void onResult(MeridianLocation location) {
                // Update UI with new Location
            }

            @Override
            public void onError(LocationRequest.ErrorType location) {
                // Notify user that there was an error retrieving the location

            }
        });
    }

    @Override
    public void onPause() {
        if (request != null && request.isRunning()) {
            request.cancel();
            request = null;
        }
        super.onPause();
    }

Get Continuous Updates About a User's Location

MeridianLocationManager determines the user's location by gathering all available location data for your Meridian-powered app. Implement the MeridianLocationManager.LocationUpdateListener interface to get callbacks as new locations are gathered.

// In an Activity that implements the LocationUpdateListener interface

private static final EditorKey appKey = new EditorKey("5468665088573440");
    private MeridianLocationManager locationHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        locationHelper = new MeridianLocationManager(this, appKey, this);
        // Build out the UI ...
    }

    @Override
    protected void onStart() {
        locationHelper.startListeningForLocation();
        super.onStart();
    }

    @Override
    protected void onStop() {
        locationHelper.stopListeningForLocation();
        super.onStop();
    }

    @Override
    public void onLocationUpdate(MeridianLocation location) {
        // Update the UI with the new location
    }

    @Override
    public void onLocationError(Throwable tr) {
        // Update the UI to inform the user that their location could not be determined.
    }

Location Sharing

Location sharing lets users share their location with each other. This feature is also available in our iOS and Android white-label apps.

Prerequisites

Location Sharing needs to be enabled by an admin in the Meridian Editor before you can use it in the SDK. If you'd like to enable Location Sharing, please email hpe-aruba-meridian-poc@hpe.com.

If you haven't already done so, you'll need to generate a valid token in the Editor. To generate one, in the sidebar menu click Permissions, and then click Application Tokens. Click Add Application + to generate a token for your app. You can name it anything.

When configuring the Meridian SDK, set the application token you generated in the Editor:

LocationSharing.init("kd84hf83ck93k39dyi3nxo3mf94");

For best results, call init from your Application class.

LocationSharing

LocationSharing is the main access point to the Location Sharing API. Use the User, Friend, Invite, and Location classes to get the Location Sharing data.

Create Accounts

// Creates a location sharing user. All users require a first name. Last name is optional.
User sampleUser = new User();
sampleUser.setFirstName("Sample User");

LocationSharing.shared().createUser(sampleUser, new LocationSharing.Callback<User>() {
    @Override
    public void onSuccess(User user) {
      // ...
    }

    @Override
    public void onError(LocationSharingException t) {
      // ...
    }
});

Start Location Sharing

LocationSharing.shared().startPostingLocationUpdates(applicationContext);

Stop Location Sharing

LocationSharing.shared().stopPostingLocationUpdates(applicationContext);

Check Location Sharing Status

LocationSharing.shared().isUploadingServiceRunning()

// example: button that performs start and stop sharing location
if (LocationSharing.shared().isUploadingServiceRunning()) {
    LocationSharing.shared().stopPostingLocationUpdates(applicationContext);
} else {
    LocationSharing.shared().startPostingLocationUpdates(applicationContext);
}

Start and Stop Location Sharing Listener

LocationSharing.shared().addListener(new LocationSharing.Listener() {
    @Override
    public void onPostingLocationUpdatesStarted() {
        // ...
    }

    @Override
    public void onPostingLocationUpdatesStopped() {
        // ...
    }
});

Create an Invite

LocationSharing.shared().createInvite(new LocationSharing.Callback<Invite>() {
    @Override
    public void onSuccess(Invite invite) {
        // ...
        // At this point, you can share the invite url. (invite.getShareUrl())
    }

    @Override
    public void onError(LocationSharingException e) {
        // ...
    }
});

Get the Current Friend List

LocationSharing.shared().getFriends(new LocationSharing.Callback<List<Friend>>() {
    @Override
    public void onSuccess(List<Friend> friendsList) {
        // ...
    }

    @Override
    public void onError(LocationSharingException e) {
        // ...
    }
});

createInvite and getFriends

LocationSharing keeps a copy of the current user, for use by calls like createInvite and getFriends. The implementor is responsible for storing the current user and setting it to the LocationSharing object.

When an account is created, the new user is automatically set to the current user.

Store the New User

After creating the new user, store it:

LocationSharing.shared().createUser(newUser, new LocationSharing.Callback<User>() {
    @Override
    public void onSuccess(User user) {
      OurOwnStorage.save(newUser);
    }

    @Override
    public void onError(LocationSharingException t) {
      // ...
    }
});

Set the Current User

If a user restarts the app, the current user needs to be set again. This is equivalent to a login operation.

LocationSharing.shared().setCurrentUser(OurOwnStorage.retrieveLocationSharingUser());

You can use the LocalSearch classes to find points of interest near a user's location. You'll usually use this in conjunction with MeridianLocationManager so that you can provide a location and nearby placemarks.

For best performance, limit local search results to 20 or less.

// This Activity implements the LocalSearch.LocalSearchListener interface

private void findNearbyCafe() {
    LocalSearch localSearch = new LocalSearch.Builder()
        .setQuery("Cafe")
        .setApp(appKey)
        .setLocation(locationManager.getLocation())
        .setListener(this)
        .setLimit(20)
        .build();

    localSearch.start();
}

@Override
public void onSearchComplete(LocalSearchResponse response) {
    for (LocalSearchResult result : response.getResults()) {
        Log.i(TAG, String.format("%s is %f seconds away", result.getPlacemark().name, result.time / 1000));
    }
}

@Override
public void onSearchError(Throwable tr) {
    Log.e(TAG, "Search error", tr);
}

Asset Tracking

Asset Tracking is a feature that uses Aruba Tags hardware to track valuable locations on a map. You can use the SDK to get Tag location updates and render Tag locations on a map.

Get Tag Updates

Before you can get Tag updates, you'll need to create an instance of Asset Tracking's main class, TagStream.

First, create your own instance of TagStream using its Builder:

tagStream = new TagStream.Builder()
                        .setMapKey(yourMapKeyHere)
                        .setListener(this)
                        .build();

With the TagStream instance created, call the following method to get updates on your tagged assets:

tagStream.startUpdatingTags();

To stop getting updates:

tagStream.stopUpdatingTags();

The TagStream builder class takes a listener that will be used to pass information about Tags to your code. The listener must implement two methods:

void onTagsUpdated(List<TagBeacon> tags);
void onTagsRemoved(List<TagBeacon> tags);

The first method will be called when we get information about new Tags or when existing Tags are updated. This list of Tags will contain all Tags.

The second method will get called when a Tag is removed. This list of Tags will contain the deleted Tags.

Render Tags on the Map

Use the marker TagMarker to render your Tags on a map. It will show a generic asset icon. Create a marker in the following way:

tagMarker = new TagMarker(getContext(), tag);

The marker gets the name and position of the marker from the tag object. At that point, you can add it to your map.

You can find working examples of this in the Samples app with TagsFragment and SingleTagFrament.