Repository navigation
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, and placemarks 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.
Go here to download the latest version of the Android SDK.
The Meridian Samples app is included with the Android SDK to demonstrate common Meridian features and workflows.
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.
The Meridian SDK supports devices with Android 11, API Level 30 and higher.
The Meridian SDK targets the latest version of Android
Complete these steps to add the AAR file to your Android project.
-
Choose a location for
meridian-x.y.z.aar. -
Edit your app’s
build.gradlefile to add the relative path to themeridian-x-y-z.aarfile location insideflatDir { }, where our app will look for dependencies. Intorepositories { }, insert:
repositories {
google()
mavenCentral()
// Tell gradle to look in our parent directory for the meridian AAR package.
flatDir {
dirs '[relative file path to the AAR directory]'
}
}To use the Meridian SDK classes in your project, add the implementation information for the dependencies to the build.gradle file. These are the meridian-x.y.z.aar file and two required external dependencies.
To find the project’s current dependencies, look in build.gradle in the MeridianSamples directory.
In the build.gradle file inside dependencies { }, insert implementation for the meridian-x.y.z.aar file and the three required external dependencies:
dependencies {
implementation 'com.arubanetworks.meridian:meridian:x.y.z@aar'
// Google Support Libraries
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
// Required for GPS on newer Android devices.
implementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'com.google.android.material:material:1.7.0'
// The SDK also has these external dependencies that you'll need to include:
implementation 'com.android.volley:volley:1.2.1'
implementation 'com.squareup:otto:1.3.8'
implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'org.greenrobot:eventbus:3.3.1'
implementation 'com.lemmingapex.trilateration:trilateration:1.0.2'
}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.
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"/>
<!-- For using location derived from bluetooth beacons on Android 12 and later -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN/>
To enable Meridian SDK's location-aware features (e.g. location sharing) in the background, add the following permission to your project’s AndroidManifest.xml file and request the permission from the user as required for the Android version:
<!-- For using bluetooth in the background -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Before you can use the features of the Meridian SDK, you’ll need to create an instance of MeridianConfig. This will configure the SDK.
Meridian uses token-based authentication. In order for your Meridian-powered app to communicate with the Editor, you’ll need to specify a Meridian token when initializing the SDK.
Put the following line in the onCreate method of your Application class or main activity.
// Configure Meridian
Meridian.configure(this, YOUR_EDITOR_TOKEN);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.
// Create a key representing your app in the Meridian Editor.
EditorKey appKey = new EditorKey("5809862863224832");
// Create a key representing a map in the Meridian Editor.
EditorKey mapKey = EditorKey.forMap("5668600916475904", appKey.getId());
// Create a key representing a placemark.
EditorKey placemarkKey = EditorKey.forPlacemark("5668600916475904_5709068098338816", 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());You can use MapFragment to provide a self-contained interface for maps and directions. Simply initialize the fragment with a valid EditorKey:
// Create a key representing your app in the Meridian Editor.
EditorKey appKey = new EditorKey("5809862863224832");
// Create a key representing a map in the Meridian Editor.
EditorKey mapKey = EditorKey.forMap("5668600916475904", appKey.getId());
MapFragment mapFragment = new MapFragment.Builder().setMapKey(mapKey).build();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.
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/destroy events to the MapView to ensure resources are managed properly.
// Using pause/resume/destroy events with a MapView in your activity
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EditorKey appKey = new EditorKey("5809862863224832");
mapView = new MapView(this);
mapView.setAppKey(appKey);
// If you want to handle MapView events
mapView.setListener(this);
// Set the map to load
mapView.setMapKey(EditorKey.forMap("5668600916475904", 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();
}
@Override
protected void onDestroy() {
super.onDestroy();
// MapView needs to know when its host activity is destroyed
mapView.onDestroy();
}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, you can use
Meridian.getShared().setRerouteDistance().
Related to this, you can also use
Meridian.getShared().setRouteSnapDistance()to configure the distance from the route at which the user's blue dot will be "snapped" to the determined route. This value is 5 meters by default.
// 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();
}
}
}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("5809862863224832");
@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();
}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("5809862863224832");
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 lets users share their location with each other.
Location Sharing requires a blue dot subscription.
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 is the main access point to the Location Sharing API. Use the User, Friend, Invite, and Location classes to get the Location Sharing data.
// 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 LocationSharing.shared().startPostingLocationUpdates(applicationContext); LocationSharing.shared().stopPostingLocationUpdates(applicationContext); 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);
} LocationSharing.shared().addListener(new LocationSharing.Listener() {
@Override
public void onPostingLocationUpdatesStarted() {
// ...
}
@Override
public void onFriendsUpdated(ListInvitations expire after 7 days.
LocationSharing.shared().createInvite(new LocationSharing.Callback LocationSharing.shared().getFriends(new LocationSharing.CallbackLocationSharing 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.
After creating the new user, store it:
LocationSharing.shared().createUser(newUser, new LocationSharing.CallbackIf 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 is a feature that uses BLE 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.
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(ListThe 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.
To dispose of the TagStream:
tagStream.dispose();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.