Android Location Using Google Play Services

Filed Under: Android

Welcome to android location using google play services example. Today we will learn how to use Google Play services API to retrieve your mobile location with example app.

Android Location API Overview

In the previous tutorial, we retrieved the user’s location using Android’s Location API that was available since Android’s API 1. So why was there a need for introducing Google Play Location Services? Why hasn’t Google enhanced Android’s Location API? What are the advantages of Google Play Location Services over the default Android Location API? Let’s discuss these questions to get a clear idea.

Need for introducing Google Play Location Services

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. Furthermore, it provides new features such as user’s activity detection that wasn’t available in the Android Framework’s Location API. Currently, Google provides 5 user states which are In Vehicle, On Bicycle, On Foot, Still, and Tilting, which are good enough to detect user’s activity, and to provide right content according to user’s status.
Another feature it provides is Geofencing API that is used to notify a user entering or exiting a particular area.
The above advantages clearly indicate why Google Location Services API(also known as FusedLocationProviderApi) is Google’s recommended way of getting a user’s location. It provides the best accuracy based on our needs.

Why hasn’t Google enhanced Android’s Location API?

From a technical point of view, Google hasn’t improved Android’s Location API since Android has an independent update roll-out feature that lies in the hands of the smartphone manufacturer. Google has less control over it and hence decided to shift to a new API instead.

There are few important classes that are used to get the location:

  • LocationRequest : A data object that contains quality of service parameters for requests to the FusedLocationProviderApi. LocationRequest objects are used to request a quality of service for location updates from the FusedLocationProviderApi.
  • FusedLocationProviderApi : The main entry point for interacting with the fused location provider. The methods must be used in conjunction with a GoogleApiClient client which we’ll look into shortly.
  • com.google.android.gms.location.LocationListener : The LocationListener interface is used for receiving notifications from the FusedLocationProviderApi when the location has changed. The method onLocationChanged is invoked if the LocationListener has been registered with the location client using the requestLocationUpdates(GoogleApiClient, LocationRequest, LocationListener) or requestLocationUpdates(GoogleApiClient, LocationRequest, LocationListener, Looper) methods.

To use Google Play’s Location Services API we need to call GoogleAPIClient first.

GoogleAPIClient

GoogleAPIClient allows us to call multiple Google APIs using a single call.
Following is an example snippet to invoke the GoogleAPIClient with two APIs : Location Services and Drive API.


GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)
                    .addApi(Drive.API)
                    .addScope(Drive.SCOPE_FILE)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();

mGoogleApiClient.connect();

GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener are implemented for addConnectionCallbacks and addOnConnectionFailedListener. onConnected() method that belongs to the GoogleApiClient.ConnectionCallbacks interface gets invoked when connections to all the APIs are established. onConnectionFailed() that belongs to GoogleApiClient.OnConnectionFailedListener interface is called in case of connection failure.

Project Structure

android-fused-location-api-project-structure

Code

Add the following dependency in the build.gradle file.

compile 'com.google.android.gms:play-services:9.8.0

Add the following permissions in the AndroidManifest.xml file.


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

The activity_main.xml is given below.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.journaldev.fusedlocationprovider.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Current Location"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1"
        android:layout_centerVertical="true"
        android:id="@+id/current_location"
        android:layout_centerHorizontal="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textSize="18sp"
        android:id="@+id/latLng"
        android:layout_below="@+id/current_location"
        />


</RelativeLayout>

The MainAcitivity.java is given below.


public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, LocationListener {

    Location mLocation;
    TextView latLng;
    GoogleApiClient mGoogleApiClient;
    private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;

    private LocationRequest mLocationRequest;
    private long UPDATE_INTERVAL = 15000;  /* 15 secs */
    private long FASTEST_INTERVAL = 5000; /* 5 secs */

    private ArrayList permissionsToRequest;
    private ArrayList permissionsRejected = new ArrayList();
    private ArrayList permissions = new ArrayList();

    private final static int ALL_PERMISSIONS_RESULT = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        latLng = (TextView) findViewById(R.id.latLng);

        permissions.add(ACCESS_FINE_LOCATION);
        permissions.add(ACCESS_COARSE_LOCATION);

        permissionsToRequest = findUnAskedPermissions(permissions);
        //get the permissions we have asked for before but are not granted..
        //we will store this in a global list to access later.


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


            if (permissionsToRequest.size() > 0)
                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
        }



            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
    }


    private ArrayList findUnAskedPermissions(ArrayList wanted) {
        ArrayList result = new ArrayList();

        for (String perm : wanted) {
            if (!hasPermission(perm)) {
                result.add(perm);
            }
        }

        return result;
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (!checkPlayServices()) {
            latLng.setText("Please install Google Play services.");
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {


        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);


        if(mLocation!=null)
        {
            latLng.setText("Latitude : "+mLocation.getLatitude()+" , Longitude : "+mLocation.getLongitude());
        }

        startLocationUpdates();

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

    @Override
    public void onLocationChanged(Location location) {

        if(location!=null)
            latLng.setText("Latitude : "+location.getLatitude()+" , Longitude : "+location.getLongitude());



    }

    private boolean checkPlayServices() {
        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
        int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (apiAvailability.isUserResolvableError(resultCode)) {
                apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
                        .show();
            } else
                finish();

            return false;
        }
        return true;
    }

    protected void startLocationUpdates() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(UPDATE_INTERVAL);
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getApplicationContext(), "Enable Permissions", Toast.LENGTH_LONG).show();
        }

        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);


    }

    private boolean hasPermission(String permission) {
        if (canMakeSmores()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
            }
        }
        return true;
    }

    private boolean canMakeSmores() {
        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
    }


    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        switch (requestCode) {

            case ALL_PERMISSIONS_RESULT:
                for (String perms : permissionsToRequest) {
                    if (!hasPermission(perms)) {
                        permissionsRejected.add(perms);
                    }
                }

                if (permissionsRejected.size() > 0) {


                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                                requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                            }
                                        }
                                    });
                            return;
                        }
                    }

                }

                break;
        }

    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(MainActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopLocationUpdates();
    }


    public void stopLocationUpdates()
    {
        if (mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi
                    .removeLocationUpdates(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
        }
    }
}

mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); is used to get the last known location from the location services.

Following is the output when the application is run on a genymotion emulator.
android-fused-location-api-output-1

Our emulator can’t fetch the location since Google Play services isn’t installed.
Let’s see what the output looks like on a smartphone.
android location, android location using google play services
The Latitude and Longitude texts in the above application are updated every 5-15 seconds.

This brings an end to this tutorial. You can download the Android FusedLocationProvider Project from the link below.

Comments

  1. Shashank Agarwal says:

    Hi Anupam,
    Compiler is showing error for below line of code:

    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

    error: no suitable method found for requestLocationUpdates(GoogleApiClient,LocationRequest,MainActivity)
    method FusedLocationProviderApi.requestLocationUpdates(GoogleApiClient,LocationRequest,LocationListener) is not applicable
    (argument mismatch; MainActivity cannot be converted to LocationListener)
    method FusedLocationProviderApi.requestLocationUpdates(GoogleApiClient,LocationRequest,PendingIntent) is not applicable
    (argument mismatch; MainActivity cannot be converted to PendingIntent)

  2. M says:

    Hi,
    running your code, I’ve got this error:
    Cause: method ID not in [0, 0xffff]: 65536
    in Android 3.2.1.
    When did you write your code?
    Thanks,
    M

  3. rahul patel says:

    i have executed the code but the error coming in onRequestPermisionsResults saying imcomatible type for (String perms) what to do ??

  4. Vijay says:

    Hi,
    1) Why are you save the permissions in Arraylist?? If I did not save and check directly, then what will happen??
    2) If I don’t have internet and GPS what will happen?

    1. Anupam says:

      Hi Vijay,
      When you’re dealing with more than one values(permissions here) you should ideally use a data structure. You can use a String array if you want to.
      If you don’t have the internet/gps the location won’t be fetched.

  5. Jessica says:

    Great Stuff!

    Thanks,
    Jessica (www.becomeceleb.com?profile/journaldev)

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages