Using the Android Fused Location Provider

May 27, 2016 at 2:29 pm 2 comments

location-clipart-map_pinIn this post, I’ll walk you through the code in a very minimal app that just demonstrates getting locaiton. This app was created using the Xamarin Android platform, so it is written in C# but still uses the Fused Location Provider API which is provided by Google Play Services . My code is available on GitHub in a Xamarin Studio / Visual Studio solution: Geolocation Demo.

Creating the Android Project

Create an Android App project as you normally would. I like to set the target API to be the latest stable version, which is currently Marshmallow, API level 23. You could set the minimum API to the lowest API level that supports Google Play Services, which is Gingerbread, API 9. But, I set it to KitKat, API 19, because that is the lowest API level that I have an emulator configured for 🙂

You will need to go into the project properties for Android Application and check the Required permissions for AccessCoarseLocation and AccessFineLocation.Access Coarse and Fine Location

Declaring a Location-Aware Activity Class

The first step is to declare a custom Activity class that inherits not only from Activity, but also from these three interfaces, which define signatures for the methods shown below:

  • GoogleApiClient.IConnectionCallbacks
    • public void OnConnected (Bundle connectionHint)
      Called when a connection is made to the GoogleApiClient. Note that this connection is to all of the avialable Google Services APIs, not just the location api.
    • public void OnConnectionSuspended (int cause)
      Called when the client is temporarily disconnected; usually due to problems with a remote service.
  • GoogleApiClient.IOnConnectionFailedListener
    • public void OnConnectionFailed
           (Android.Gms.Common.ConnectionResult result)

      Called when an error occurs when attempting to connect to the client service.
  • ILocationListener 
    • public void OnLocationChanged
           (Android.Locations.Location location)

      Called when the Fused Location Provider reports a change of locaiton. Of the three interfaces, this is the only one specific to location.

Here’s how the code for our MainActivity looks so far:

 using Android.App;
 using Android.Gms.Location;
 using Android.Gms.Common.Apis;

namespace FusedLocationProviderDemo
 {
     [Activity (Label = "Fused Location Provider Demo",
 MainLauncher = true, Icon = "@mipmap/icon")]
     public class MainActivity : Activity,
                      GoogleApiClient.IConnectionCallbacks,
                      GoogleApiClient.IOnConnectionFailedListener,
                      ILocationListener
 {

After writing the empty class declaration you will see an error saying that the interface needs to be implemented. Both Visual Studio and Xamarin Studio have a refactor options to Implement Interfaces. The interfaces will be implemented as stubs. Later you will put some code into OnLocationChanged.

Every App Needs a UI

Next we’ll add code for our UI, but this isn’t anything unique to a location aware app.

Here’s the layout in AXML:

 <?xml version="1.0" encoding="utf-8"?>
  <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <Button
         android:id="@+id/lastLocationButton"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="Get last location" />
     <Button
         android:id="@+id/locationUpdateButton"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="Get location updates" />
     <TextView
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="Not connected yet" 
         android:id="@+id/locationTextView" />
 </LinearLayout> 

Here’s what the layout looks like:

Geolocation Demo UI

We’ll need a couple more using statements for the UI related code:

 using Android.Widget;
 using Android.OS;

Here’s the C# UI related code that goes in the MainActivity class:
(I snuck in a declaration for the GoogleApiClient too, which I’ll explain later)

    Button lastLocationButton;
    Button locationUpdatesButton;
    TextView locationTextView;           
    GoogleApiClient apiClient;    
     
     protected override void OnCreate (Bundle savedInstanceState) {
         base.OnCreate (savedInstanceState);             
         SetContentView (Resource.Layout.Main);

Setting up the Google Play Services Client API

Now, we’ll add the following statement:

     apiClient = new GoogleApiClient.Builder (this, this, this)
          .AddApi (LocationServices.API).Build ();
     apiClient.Connect();

Let’s take it apart and look at it. We already declared GoogleApiClient apiClient as a field of the class. Now we’re creating an instance. We do this using the static Builder method in the GoogleApiClient class. This method takes the following arguments:

Builder (Context context, // Activity contenxt GoogleApiClient.IConnectionCallbacks, // Object derived form interface GoogleApiClient.IOnConnectionFailedListener) // Same, for this interface

Notice that the arguments for all the Builder parameters are this because the object we are in is the MainActivity, and it implements the two interfaces. The Builder class is used to configure the Google Play Services (GPS) API client. The APIs are modularized so that you don’t have to include all the GPS APIs in yuour app, just the ones you need. In our case we just need the LocationServices API, so tht is what we add. Finally, we connect our client to Google Play Services.

 

Get the Last Known Location

When we click the button, we’ll call GetLastLocation on the Fused Location API. (The code for this is shown below.) This method will only return a loction if some other activity has already requested location updates. So you may need to run some other app, like Google Maps, so that there will be a last known location available. Or once the rest of this app is complete, click the Get Location Updates Button.

 lastLocationButton = 
           FindViewById<Button> (Resource.Id.lastLocationButton);
  locationTextView = FindViewById<TextView> 
                              (Resource.Id.locationTextView);
  lastLocationButton.Click += delegate {
     Android.Locations.Location location = 
           LocationServices.FusedLocationApi.GetLastLocation (apiClient);
   locationTextView.Text = "Getting last location";
   if (location != null)  {
       locationTextView.Text = "Last location:\n";
       locationTextView.Text += "Latitude: " + 
                    location.Latitude.ToString() + "\n";
       locationTextView.Text += "Longitude: " + 
                    location.Longitude.ToString() + "\n";
       locationTextView.Text += "Provider: " + 
                    location.Provider.ToString();
     }
     else  {
         locationTextView.Text = "No location info available";
     }
  };

You have enough code written now to test the app. But first you need to install Google Play Services in your emulator. When you run the app, and click the “Get last location button”, you should see a result like this:

Get Last Location

Get Continuous Location Updates

Now we’ll add code that will use the other button to get continuous location updates. The code to initiate location updates goes in a button click event handler inside of OnCreate:

locationUpdateButton = 
          FindViewById<Button> (Resource.Id.locationUpdateButton);

locationUpdateButton.Click += async delegate {
   if (apiClient.IsConnected) {
       locationTextView.Text = "Requesting Location Updates";
       var locRequest = new LocationRequest();
 
    locRequest.SetPriority(100);  // PRIORITY_HIGH_ACCURACY 
    locRequest.SetFastestInterval(500); // update interval in ms
    locRequest.SetInterval(1000); 
 
    await LocationServices.FusedLocationApi.RequestLocationUpdates 
                                        (apiClient, locRequest, this);
     }
     else {
          locationTextView.Text = "Client API not connected";
     }
};

This event handler delegate has the async modifier in front of it meaning that this method will run on a separate thread and immediately return execution control to its caller. The async method will run until it gets to the statement with the await modifier, executiion will pause on this statement until the statement returns.

For those of you who are not familiar with asynchronous programming, the purpose here is to run code that might take a long time to execute on a separate thread and have it call some callback method when it’s done. In this case the time-consuming event is getting the location and callbacks are made to OnLocationChanged. 

The Fused Location Provider determines which location service to use (GPS, Network, or Passive) based on priority rather than by specifying a service directly. Here are the options:

  • 100: High Accuracy – Highest accuracy and highest battery drain.
  • 102: Balanced Power/Accuracy – Coarse accuracy of about 100 meters and medium battery drain. This is the default setting.
  • 104: Low Power – Coarse accuracy of about 10 kilometers and lowest battery drain.
  • 105: No Power – Similar to the Passive Provider, only provides location updates when requested by other activities.

Each time an update is recieved, OnLocationChanged will be called. We’ll add the following code to the MainActivity class:

public void OnLocationChanged (Android.Locations.Location location{
 
    locationTextView.Text = "Location updates:\n";
     DisplayLocation (location);
}
 
private void DisplayLocation(Android.Locations.Location location{
    if (location != null{
       locationTextView.Text += "Latitude: " + 
                    location.Latitude.ToString() + "\n";
       locationTextView.Text += "Longitude: " + 
                     location.Longitude.ToString() + "\n";
        locationTextView.Text += "Provider: " + 
                     location.Provider.ToString()}
     else {
        locationTextView.Text = "No location info available";
     }
}

That’s it!

Advertisements

Entry filed under: C Sharp, C#, Mobile, Programming, Xamarin. Tags: , , , .

The Google Play Services APIs for Android Is It possible to run a 100% Remote Team?

2 Comments Add your own

  • 1. Mike  |  July 31, 2016 at 12:46 am

    Do you have an example how to implement getting location updates in the background (when app is killed)?

    Reply
    • 2. Bahrom  |  August 2, 2016 at 5:21 pm

      I don’t. Does anyone else have an example they can post a link to?

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Bird’s Bits

Computers, software & the Internet

Recent Posts

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 37 other followers


%d bloggers like this: