{{announcement.body}}
{{announcement.title}}

Gathering the Android Device Position With the HERE Positioning API

DZone 's Guide to

Gathering the Android Device Position With the HERE Positioning API

Learn how to calculate Android device position with the HERE positioning API.

· Java Zone ·
Free Resource

About a week ago, I had written a tutorial that leveraged the HERE Places API for Android. This tutorial titled, Search for Points of Interest in an Android Mobile Application, searched for these points of interest using a fixed location relative to those places. In other words, the places found did not accurately reflect the distance from the actual device location. In a realistic, production-ready scenario, we’d want to find places relative to the position of the actual Android device. Luckily, we can find the device position using the HERE Positioning API for Android.

In this tutorial, we’re going to revisit the previous tutorial regarding the HERE Places API, but we’re going to take it to the next level with the HERE Positioning API.

To get an idea of what we want to accomplish, take a look at the following animated image:

android-positioning-places

The application centers the map on our current position made obvious with an indicator. We can then search for places nearby to the current position. In the simulator, we can change the position and the map will center itself again. This simulates what would happen if there was movement with the Android device.

Before going forward, it is important to note that we won’t be discussing how to configure HERE without our Android project or how to display a map. If you’re stuck and need help with the setup, I encourage you to check out my previous tutorial titled, Getting Started with HERE Maps in an Android Application.

Listening and Displaying Changes to the Device Location

Rather than constantly checking to see if the position has changed for the mobile device, we’re going to configure a listener that triggers when these changes happen. However, before we do that, we need to add a few permission requirements to our manifest:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>


If you’re targeting a more modern version of Android, you may also have to request permissions at runtime as well. With the permissions in place, we can start configuring a listener within our application.

If you went through my previous tutorial, you should have the following variables or some variation of them:

private Map map = null;
private MapFragment mapFragment = null;
private EditText editText = null;
private ArrayList<MapObject> markers = null;
private PositioningManager positioningManager = null;
private PositioningManager.OnPositionChangedListener positionListener;
private GeoCoordinate currentPosition = new GeoCoordinate(37.7397, -121.4252);


The difference this time around is the positioningManager, positionListener, and currentPosition variables. The positionListenerwill let us listen for changes, but the positioningManager will allow us to start and stop listening, which could be useful when suspending or resuming the application. The currentPosition is created so we can globally keep track of the current position to be used when searching for points of interest.

The next step is to configure the listener and start listening:

mapFragment.init(new OnEngineInitListener() {
    @Override
    public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
        if (error == OnEngineInitListener.Error.NONE) {
            map = mapFragment.getMap();
            map.setCenter(new GeoCoordinate(37.7397, -121.4252, 0.0), Map.Animation.NONE);
            map.setZoomLevel((map.getMaxZoomLevel() + map.getMinZoomLevel()) / 2);
            positioningManager = PositioningManager.getInstance();
            positionListener = new PositioningManager.OnPositionChangedListener() {
                @Override
                public void onPositionUpdated(PositioningManager.LocationMethod method, GeoPosition position, boolean isMapMatched) {
                    currentPosition = position.getCoordinate();
                    map.setCenter(position.getCoordinate(), Map.Animation.NONE);
                }
                @Override
                public void onPositionFixChanged(PositioningManager.LocationMethod method, PositioningManager.LocationStatus status) { }
            };

            try {
                positioningManager.addListener(new WeakReference<>(positionListener));
                if(!positioningManager.start(PositioningManager.LocationMethod.GPS_NETWORK)) {
                    Log.e("HERE", "PositioningManager.start: Failed to start...");
                }
            } catch (Exception e) {
                Log.e("HERE", "Caught: " + e.getMessage());
            }
            map.getPositionIndicator().setVisible(true);
        }
    }
});


After the mapping engine finishes the initialization, we can define the overrides for the onPositionUpdated and the onPositionFixChangedmethods. The onPositionFixChanged is a method for if we change between GPS, network, or other, while the onPositionUpdated is in actual changes to the latitude and longitude of the device.

When the position changes, we’re going to update the current position and center the map.

Finally, we can add the listener to our positioningManager and start listening. Optionally, we can make the position indicator visible on the map.

This takes us to the next step, which is almost exactly what we’ve already seen.

Searching for Places Relative to the Current Device Location

With the current device position being tracked, we can make use of it in our search method that was defined in the previous tutorial.

Take the new and improved search method:

public void search(String query) {
    if(!markers.isEmpty()) {
        map.removeMapObjects(markers);
        markers.clear();
    }
    try {
        DiscoveryRequest request = new SearchRequest(query).setSearchCenter(currentPosition);
        request.setCollectionSize(5);
        ErrorCode error = request.execute(new ResultListener<DiscoveryResultPage>() {
            @Override
            public void onCompleted(DiscoveryResultPage discoveryResultPage, ErrorCode error) {
                if (error != ErrorCode.NONE) {
                    Log.e("HERE", error.toString());
                } else {
                    for(DiscoveryResult discoveryResult : discoveryResultPage.getItems()) {
                        if(discoveryResult.getResultType() == DiscoveryResult.ResultType.PLACE) {
                            PlaceLink placeLink = (PlaceLink) discoveryResult;
                            MapMarker marker = new MapMarker();
                            marker.setCoordinate(placeLink.getPosition());
                            markers.add(marker);
                            map.addMapObjects(markers);
                        }
                    }
                }
            }
        });
        if( error != ErrorCode.NONE ) {
            Log.e("HERE", error.toString());
        }
    } catch (IllegalArgumentException ex) {
        Log.e("HERE", ex.getMessage());
    }
}


In case you don’t see it, the only thing that changed is the following line:

DiscoveryRequest request = new SearchRequest(query).setSearchCenter(currentPosition);


Instead of hardcoding a current position to go with the query, we’re using the current position as defined by the HERE Positioning API. When performing the search completes, any places returned are added to the map as markers.

The actual search is performed after the input field is populated and the enter key is pressed, as seen below:

editText.setOnKeyListener(new View.OnKeyListener() {
    public boolean onKey(View view, int keyCode, KeyEvent keyevent) {
        if ((keyevent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
            search(editText.getText().toString());
            editText.setText("");
            return true;
        }
        return false;
    }
});


To clean up any loose ends, the editText and mapFragment are variables that are mapped to the components found in your XML layout.

Conclusion

You just saw how to use the HERE Positioning API within your Android mobile application. While the example was familiar, the emphasis was around configuring a listener position updates on the device and using that position to move on the map.

As good practice, you should take into consideration the lifecycle events of Android. If you don’t specifically stop the listener when the application enters the background, battery consumption could be more than your users want.

Topics:
java ,android ,android app development ,tutorial ,location ,map ,android device position ,HERE positioning API

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}