Places Android SDK

On this page you can see the functions and the main resources to make using of the Onirix Places Android SDK.

Create a new Android project

We will start creating a new Android project with Android Studio.

Set minimum API level to 19 or above (Android 4.4: Kit Kat)

Import Onirix SDK

Now, we have to import the onirix-places android library, for that we can add a custom maven repository under build.gradle (project name). Repositories tag should contain the following (order is important for dependency resolution)

allprojects {
    repositories {               
        google()
        jcenter()
        maven {
            url 'http://archiva.neosentec.com/repository/internal/'
        }      
    }
}

Adding onirix-places library is as simple as writing 'com.onirix:places:1.0.0' inside build.gradle (app) dependencies tag. Here it is a sample build.gradle (app) with a minimal working configuration:

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.onirix.placesexample"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding {
        enabled = true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.onirix:places:1.1.0'
}

We will also have to add the following lines inside the gradle.properties file:

android.enableJetifier=true
android.useAndroidX=true

We should also change the default theme of your application to something like Theme.AppCompat.Light.DarkActionBar, that extends from Theme.AppCompat.

And finally, we should configure any Activity as the Augmented Reality screen. For that it must extend from "MapActivity" and call initialize and loadMap methods inside onCreate lifecycle function. Here it is a fully working example:

public class MainActivity extends MapActivity {

    private static final String PROJECT_KEY = "<YOUR_PROJECT_KEY>";
    private static final String MAP_OID = "<YOUR_MAP_OID>";

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

        // Keep screen on
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        // Immersive screen
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_FULLSCREEN |
                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        );

        // Configure the map with a maximum vision distance of 5km
        MapConfig mapConfig = new MapConfig(this).withVisionDistance(5000);

        // Configure place parameters
        PlaceConfig placeConfig = new PlaceConfig(this)
                .withShowDistance(true)
                .withDistanceFontColor(0xFFFFFFFF)
                .withDistanceBackgroundColor(0x00FFFFFF)
                .withNameInactiveFontColor(0xFFFFFFFF)
                .withNameInactiveBackgroundColor(0xFFFF5D5D)
                .withNameBorder(0, 0, 10);

        // By now, all places created within Onirix Studio have "default" category
        mapConfig.setConfigForCategory("default", placeConfig);
        loadMap(MAP_OID, PROJECT_KEY, mapConfig);
    }

    @Override
    public void onMapLoaded(Map map, String error) {

        if (map != null) {

            // Map loaded, from this point you can add new places
            Place place = new Place(
                "New Place",
                "oid",
                new com.onirix.places.model.Location(43.422573, -5.826327),
                0,
                0,
                null
            );

            // If you want to load an overlay
            //loadOverlay("MyOverlay", PROJECT_KEY);

            // You must call reloadMap() to apply changes to the map
            reloadMap();
        }
        else {
            // If there is any problem loading the map, it will be null and an error message will be populated
            Log.d(getLocalClassName(), "An error ocurred when loading the map: " + error);
        }
    }

    @Override
    public void onLocationUpdate(Location location) {
        // Callback to get location updates
        Log.d(getClass().getSimpleName(), "Location received. Accuracy: " + location.getAccuracy());
    }

    @Override
    public void onPlaceTouched(Place place) {
        // Place touch events are enabled by default, you can use this callback to check wether a Place was touched
        Log.d(getLocalClassName(), "Place touched: " + place.getName());

        // When touching a Place, we start a new route to that place
        RouteConfig routeConfig = new RouteConfig(mapConfig);
        routeConfig.addRouteComponent(new ArrowSignComponent(this));
        routeConfig.addRouteComponent(new DestinationIndicationComponent());
        routeConfig.addRouteComponent(new DirectionArrowComponent());
        initRoute(routeConfig, place.getLocation().getLat(), place.getLocation().getLng());
    }

    @Override
    public void onFocusChanged(@Nullable PlaceARWrapper newFocus) {
        // Do something when the focused POI changes
    }
}

Be aware that you should change both the APP_KEY and the MAP_NAME with your own. In addition, you should handle users permissions for at least camera and location, or above code would not be able to work. You can also customize map and pois with several parameters. Full configuration list below:

Map configuration

MapConfig class uses a builder pattern with the following methods:

Method Description Default Value
withPOIDrawManager(POIDrawManager) Sets the POIDrawManager instance that tells the engine how big to draw a POI depending on its distance POIDrawManager.DEFAULT
withRadarDistance(int) Sets the radius (meters) which the radar will draw 200
withShowRadar(boolean) Sets whether the radar should be shown or not true
withUpdateRadius(float) Sets the radius to download the POIs 2000
withUpdateRatio(float) Sets the factor of the radius above when to redownload the area 0.33
withUseCrosshair(boolean) Sets whether to use the default crosshair and enable the active/inactive changes true
withUseElevations(boolean) Sets whether the user's and POI's elevation will be taken into account or not true
withUsePoiDataPanel(boolean) Sets whether to show a vertical sliding panel when a POI is focused showing its information true
withUseUpdateRadius(boolean) Sets whether to download POIs in a circle centered in the user (true) or all of them (false) false
withVisionDistance(int) Sets the distance (meters) for visible POIs 1000

PoiDrawManager

POIDrawManager is the class in charge of deciding how big each poi is rendered. In order to use custom values, you have to pass a list of POIDistance to its constructor. Each POIDistance representes the size at which a point will be drawn at a given distance, being its first parameter the distance and the second its size multiplier (compared to how big you would see it at 1 meter away). For example, if you use new POIDistance(10, 0.5f) and the POI is 10 meters away you will see it half the size as if it was only a meter away.

Place Configuration

The PlaceConfig class also uses a builder pattern with the following methods:

Method Description Default value
withMarker(int, int) Sets the image used to represent the Place, first the active version and then the inactive version. Each will be used when the specific marker is focused by the crosshair. Must be a resource of the application. R.drawable.default_marker_active, R.drawable.default_marker_inactive
withShowMarker(boolean) Sets whether the marker should be shown or not true
withShowName(boolean) Sets whether the name of the Place should be shown or not. true
withShowDistance(boolean) Sets whether the distance to the Place should be shown or not false
withFont(Typeface) Sets the font that will be used to draw the Place name and distance (if enabled). Must be a .ttf file inside assets folder. Futura.ttf
withNameInactiveFontColor(int) Sets the foreground color of the name font. 0xDD000000 (ARGB)
withNameActiveFontColor(int) Sets the foreground color of the name font when it is active. 0xDDFFFFFF (ARGB)
withNameInactiveBackgroundColor(int) Sets the background color of the name font. 0xCCFFFFFF (ARGB)
withNameActiveBackgroundColor(int) Sets the bacground color of the name font when it is active. 0xDDEE0979 (ARGB)
withNameBorderPixels(int) Sets a border width for the name. 0
withNameBorderColor(int) Sets the border color of the name. 0xFFFFFFFF (ARGB)
withNameBorderPixels(int) Sets the border radius of the name. 30
withNamePaddingPixels(int) Sets the padding of the name. 30
withDistanceFontColor(int) Sets the foreground color of the distance font. 0xFFFFFFFF
withDistanceBackgroundColor(int) Sets the color of the distance's panel background 0xDDEE0979 (ARGB)
withDistanceBorderPixels(int) Sets a border width for the distance. 0
withDistanceBorderColor(int) Sets the border color of the distance. 0xFFFFFFFF (ARGB)
withDistanceBorderRadius(int) Sets the padding of the distance. 30
withDistancePaddingPixels(int) Sets the distance panel padding. 30
withDistanceHeight(float) Sets the distance in the y axis from the name panel to the distance panel. 0.15

A Place Configuration must be applied to a PlaceCategory. For that you can use the following methods from the MapConfig class: mapConfig.setConfigForCategory(<CATEGORY_NAME>, placeConfig); This category name can be defined from the website editor when creating new Places. By default (if not specified) this category name is "default" for every Place.

Routes

In order to be able to use routes, you will have to create nodes and ways in your map. Then, you can use the following method: initRoute(routeConfig, latitude, longitude); The sdk will then calculate the path from the node closest to you to the destination node closer to the given coordinates and then to the exact coordinates. Keep in mind that, if there are no nodes close to you or the destination, it may create a weird path.

RouteComponents

Route components are independent elements that will show information about the route. It is possible to implement your own using the RouteComponent interface. The default ones are:

ArrowSignComponent

It shows a sign indicating the direction to turn in each node. It can be straight, left or right.

DirectionArrowComponent

It indicates in which direction is the next node by drawing a blinking arrow on your feet.

DistanceComponent

It shows, under the next node, the remaining distance to reach it.

DestinationIndicationComponent

It draw a big moving arrow over the destination node.

RouteConfig

Method Description Default value
withOnRouteStarted(Runnable) Sets the callback that will be called when the route starts. null
withOnRouteFinished(Runnable) Sets the callback that will be called when the route finishes. null
withOnRouteRecalculate(Runnable) Sets the callback that will be called when the route is recalculated. null
withRecalculationEnabled(boolean) Sets whether route recalculation is enabled or not. true
addRouteComponent(RouteComponent) Adds a new route component Empty

Route recalculation

If route recalculation is enabled, it detects if the user is walking outside the path and will try to recalculate the whole route in order to find a shorter path.

Listeners & Callbacks

Map callbacks

Method Description
onMapLoaded(Map map, String error) Called when the map is loaded. If there is any problem, map will be null and an error message will be populated.
onLocationUpdate(Location location) Called when the a new location arrives from GPS or Network providers.
onPlaceTouched(Place place) Called whenever a Place is touched, with the place itself as a parameter.
onFocusChanged(PlaceARWrapper placeWrapper) Called whenever a Place enters the crosshair on screen's center.

Customization

Add your own logo to the view

We've added an empty ImageView inside the activity layout with ID "logoIcon". Therefore you are able to locate it within your activity and put an icon on it (Logo should have square dimensions). Here it is an example (this should be called inside onCreate() lifecycle activity method):

ImageView logoIcon = (ImageView) findViewById(R.id.logoIcon);
logoIcon.setImageDrawable(getResources().getDrawable(R.drawable.your_icon))

Proguard rules

If you want to enable proguard in your app, you will need the following rules

-keep class com.google.**
-dontwarn com.google.**
-dontwarn okio.**
-dontwarn okhttp3.**
-keep class okhttp3.** { *; }
-dontwarn java.awt.**
-dontwarn android.graphics.**

# Keep native funcitons
-keepclasseswithmembers class * {
    native <methods>;
}

-keep class com.onirix.places_core.model.** { *; }
-keep class com.onirix.places.model.** { *; }

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
##---------------End: proguard configuration for Gson  ----------

##---------------Begin: proguard configuration for Retrofit  ----------
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.-KotlinExtensions
-dontwarn retrofit2.Platform$Java8
##---------------End: proguard configuration for Retrofit  ----------