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)
orrequestLocationUpdates(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.
1 2 3 4 5 6 7 8 9 |
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
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.
1 2 3 4 |
<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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
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.
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.
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.