Google Maps Location
12 June, 2015 - 9 min read
First tutorial and I will start with a good one. It occurred to me that the pretty little phones we have in our pockets are very good gps receivers and that location aware apps or apps that provide guidance with GPS is a blessing. Some may argue about the ethics behind such thinks but this is not the topic of the current post. So I will describe the process of using Google Maps and Android location in your app. In particular I used it on the Want Milk app. Want Milk is getting the current position of the user using the fused location API from Google and is displaying some makers on the map.
When I first did these the guide that I used was published on teamtreehouse by Ben Jakuben.
Fused location The Google Location Services API, part of Google Play Services, provides a more powerful, high-level framework that automates tasks such as location provider choice and power management. Location Services also provides new features such as activity detection that aren’t available in the framework API. Developers who are using the framework API, as well as developers who are just now adding location-awareness to their apps, should strongly consider using the Location Services API.
So this API is tied to the Google play service which means that if on a device there is no Google play installed we wont be able to get any accurate data. This means that we first have to add the Google Play Services SDK to the current SDK Manager provided by Android Studio.
After that we can go ahead and create the first template by selecting to start a new project and selecting the Google Maps Activity. Let it configure itself and wait a couple of minutes until Graddle will have built everything.
The first screen you will see one key element you should not forget, in order to start using Google Maps you will need to provide a way to authenticate your application with Google Server by providing a SHA1 key to Google. This key is generated by Android Studio and you can get it from the Google maps api.xml that is open. It will be something like this:
85:42:7E:30:G5:33:15:0A:B3:54:A9:47:31:2C:F5:C4:3F:LD:33:11
You will have to add this key together with the package name to the Google API page. Do not forget that this key is for debugging purposes which means you will have to generate a new SHA1 key for the production environment. If you look under the dependencies you will see a new line
{
compile 'com.google.android.gms:play-services:6.5.87′
}
This is how Gradle tells to compile also the play services in the current version with the project.
Let’s get to the code:
If you see your Java source file you will see a map fragment together with the onCreate methods populated for you. The code is fine and usable. What it does is simple, it calls a fragment where your map is placed and a maker is placed at a default latitude and longitude. In this case its the 0,0 position. If you compile and run this project it will generate an APK where when opened in a emulator or real device a map with a marker at the position will show up. If you change the .position(new LatLang(0,0)) to something else you will change the position of the marker.
So how do we get the current location? Simple first we have to make sure that our app permission include “ACCESS\COARSE_LOCATION” and “ACCESS_FINE_LOCATION”_. After that we should create a new object from the GoogleApiClient and finally use as described in the official documentation.
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
So what exactly are these lines doing for us? The first is to create a new GoogleApiClient object using the Builder pattern that you may have seen in Android with things like AlertDialogs. The methods are chained together to make the code easier to understand. The next two lines tell the new client that “this” current class (MapsActivity) will handle connection stuff. We’ll add that next.
The fourth line adds the LocationServices API endpoint from GooglePlayServices, and then finally the client is built for us. It’s almost ready to connect!
But first we need to implement some methods for those interfaces related to those two lines about callbacks and a listener. Getting a user’s location is an asynchronous process because it might take a little time to get the location data. We don’t want the app to be unresponsive while it’s waiting, so we do that work in the background. When that work is done in the background, it needs to get back to this main thread somehow. That’s where callbacks come in. The code we write in a callback method gets executed when that background work “calls back” with some sort of result.
We must implement
GoogleApiClient.ConnectionCallbacks,GoogleApiClient
.OnConnectionFaileListener
so that the final class will look like this:
public class MapsActivity extends FragmentActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
After that we should import the new classes to our errors will go away and implement the missing methods.
After having the basic structure in place we should organize the connecting and disconnecting on our methods onResume(). The activity may be paused at any time and when we recall the activity lifecycle process you will see that after onCreate() the onResume() is called.
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
mGoogleApiClient.connect();
}
Don’t forget to add the appropriate method on our onPause() so we disconnect from the service and don’t waste cpu cycles and battery power.
@Override
protected void onPause() {
super.onPause();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
Getting the last location
Using the Google Play services location APIs, your app can request the last known location of the user’s device. In most cases, you are interested in the user’s current location, which is usually equivalent to the last known location of the device.
on the onConnected() method add the following
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
You should always remember to incorporate a null check in case we don’t have a location and request a new one.
if (location == null) {
// Blank for a moment…
}
else {
handleNewLocation(location);
};
Handling Errors
We won’ t do anything special about the error cases, but it’ s important to know that Google Play Services includes some built-in mechanisms for handling certain errors. The first thing we need is a constant static member variable at the top of our MapsActivity class. This defines a request code to send to Google Play services, which is returned in Activity.onActivityResult():
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
Then paste the code below into the onConnectionFailed() method. There are more detailed comments in the source on GitHub, and check the documentation for a little more information.
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} else {
Log.i(TAG, "Location services connection failed with code " +
connectionResult.getErrorCode());
}
}
Requesting Location Updates
Next thing on the list is to get request those location updates so we can use them is our code. This is primary used run-tracking app or navigation application. For this we need to implement a LocationListener interface and be sure to use the com.google.android.gms.location. After that fix the error by implementing the interface methods. You will see the onLocationChanged() method. This method gets called every time a new location is detected by the Google Play service and is silently working in the background updating the location. To use it in our Activity we will use the a method we have already implemented handlNewLocation()
@Override
public void onLocationChanged(Location location) {
handleNewLocation(location);
}
Starting a request
To be able to start a request we have a method for it onLocationChanged() but we don’t have anything set up to call it. So we will create a new object LocationRequest and initialize it in on the onCreate() .
// Create the LocationRequest object
locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(10 * 1000) // 10 seconds, in milliseconds
.setFastestInterval(1 * 1000); // 1 second, in milliseconds
So what does it do? First thing we set the priority to high accuracy which means we want location updates as accurate as possible. This mode also drains the most battery. If we don’t need so high accuracy we can lower the value. Next up is the Interval which means when the location update is triggered. Higher number means more frequent updates. Lastly the last line is an option as to when receive location updates when other apps are requesting it. Since this is shared across android you can “steal the location”.
Now we just need to use this request. We use it with our GoogleApiClient using a special method from the fused location provider. Let’s add this line in our onConnected() method where we check to see if the last location is null:
@Override
public void onConnected(Bundle bundle) {
Location location = LocationServices.FusedLocationApi
.getLastLocation(mGoogleApiClient);
if (location == null) { LocationServices.FusedLocationApi
.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
} else {
handleNewLocation(location);}
}
As a reminder this code is requesting only if the last location is not known.
When finished we should remove and updates in the onPause() method
@Override
protected void onPause() {
super.onPause();
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi
.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
}
Showing a location on the map
When you scroll down the template you will see have made a setUpMap() where there is a mMap.addMarker.
The following lines of code will be added in our handleNewLocation() helper method. First, we can get the latitude and longitude coordinates of the location using these two methods from the Location class:
double currentLatitude = location.getLatitude();
double currentLongitude = location.getLongitude();
We then can create a LatLng object that stores the information.
LatLng latLng = new LatLng(currentLatitude, currentLongitude);
and change the method to
MarkerOptions options = new MarkerOptions()
.position(latLng)
.title("I am here!");
mMap.addMarker(options);
That’s all there is. Seems quite a lot of work but you can save this and whenever you need to get a user location you can use this method and implement this in your apps.