time

How to get current location (latitude, longitude) with GPS in Android

How to get current location (latitude, longitude) with GPS in Android

Android Location API can be used to track your mobile current location and show in the app. In this tutorial, you will learn how to get your current location in Android using GPS.

There are two ways to get a users location in our application:

  • android.location.LocationListener. This is a part of the Android API.
  • com.google.android.gms.location.LocationListener. This is present in the Google Play Services API.

Google officially recommends using Google Play Location Service APIs. Android Location Services API is still used to develop location-based apps for devices that don’t support Google Play Services.

LocationListener from android.location

There are three main componets of Location API:

  • Location class gives geographic location. It contains latitude and longitude etc.
  • LocationManager provides access to the system location services.
  • LocationListener used for receiving notifications from the LocationManager when the location has changed.

The Location object represents a geographic location which can consist of a latitude, longitude, time stamp, and other information such as bearing, altitude and velocity. There are following important methods which you can use with Location object to get location specific information.

  • distanceTo(Location dest). Returns the approximate distance in meters between this location and the given location.
  • getAccuracy(). Get the estimated accuracy of this location, in meters.
  • getAltitude(). Get the altitude if available, in meters above sea level.
  • getLatitude(). Get the latitude, in degrees.
  • getLongitude(). Get the longitude, in degrees.
  • getBearing(). Get the bearing, in degrees.
  • getSpeed(). Get the speed if it is available, in meters/second over ground.
  • setLatitude(double latitude). Set the latitude, in degrees.
  • setLongitude(double longitude). Set the longitude, in degrees.

LocationManger class provides access to the system location services. These services allow applications to obtain periodic updates of the device’s geographical location, or to fire an application-specified Intent when the device enters the proximity of a given geographical location.

The LocationListener class needs to implement the following methods.

  • onLocationChanged(Location location). Called when the location has changed.
  • onProviderDisabled(String provider). Called when the provider is disabled by the user.
  • onProviderEnabled(String provider). Called when the provider is enabled by the user.
  • onStatusChanged(String provider, int status, Bundle extras). Called when the provider status changes.

The LocationManager has two means of acquiring location data:

  • LocationManager.GPS_PROVIDER. Determines location using satellites. Depending on the conditions, this provider may take a while to return a location fix.
  • LocationManager.NETWORK_PROVIDER. Determines location based on the availability of nearby cell towers and WiFi access points. This is faster than GPS_PROVIDER.

Next, we’ll add the LocationListener interface to our Activity to receive periodic location updates via GPS Providers or Network Providers.

To run our GPS Location Manager application, we need to provide the permissions given below. Add the permission on 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 layout is defined below.

<?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">

    <TextView
        android:text="Latitude:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvLatitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:text="Longitude:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvLongitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:text="Time of request:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvTime"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

The MainActivity.java class is defined below.

import android.Manifest;

public class SecondActivity extends AppCompatActivity implements LocationListener {
    final String TAG = "GPS";
    private final static int ALL_PERMISSIONS_RESULT = 101;
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1;

    TextView tvLatitude, tvLongitude, tvTime;
    LocationManager locationManager;
    Location loc;
    ArrayList<String> permissions = new ArrayList<>();
    ArrayList<String> permissionsToRequest;
    ArrayList<String> permissionsRejected = new ArrayList<>();
    boolean isGPS = false;
    boolean isNetwork = false;
    boolean canGetLocation = true;

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

        tvLatitude = (TextView) findViewById(R.id.tvLatitude);
        tvLongitude = (TextView) findViewById(R.id.tvLongitude);
        tvTime = (TextView) findViewById(R.id.tvTime);

        locationManager = (LocationManager) getSystemService(Service.LOCATION_SERVICE);
        isGPS = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        isNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
        permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
        permissionsToRequest = findUnAskedPermissions(permissions);

        if (!isGPS && !isNetwork) {
            Log.d(TAG, "Connection off");
            showSettingsAlert();
            getLastLocation();
        } else {
            Log.d(TAG, "Connection on");
            // check permissions
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (permissionsToRequest.size() > 0) {
                    requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]),
                            ALL_PERMISSIONS_RESULT);
                    Log.d(TAG, "Permission requests");
                    canGetLocation = false;
                }
            }

            // get location
            getLocation();
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        Log.d(TAG, "onLocationChanged");
        updateUI(location);
    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {}

    @Override
    public void onProviderEnabled(String s) {
        getLocation();
    }

    @Override
    public void onProviderDisabled(String s) {
        if (locationManager != null) {
            locationManager.removeUpdates(this);
        }
    }

    private void getLocation() {
        try {
            if (canGetLocation) {
                Log.d(TAG, "Can get location");
                if (isGPS) {
                    // from GPS
                    Log.d(TAG, "GPS on");
                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (loc != null)
                            updateUI(loc);
                    }
                } else if (isNetwork) {
                    // from Network Provider
                    Log.d(TAG, "NETWORK_PROVIDER on");
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        loc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (loc != null)
                            updateUI(loc);
                    }
                } else {
                    loc.setLatitude(0);
                    loc.setLongitude(0);
                    updateUI(loc);
                }
            } else {
                Log.d(TAG, "Can't get location");
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

    private void getLastLocation() {
        try {
            Criteria criteria = new Criteria();
            String provider = locationManager.getBestProvider(criteria, false);
            Location location = locationManager.getLastKnownLocation(provider);
            Log.d(TAG, provider);
            Log.d(TAG, location == null ? "NO LastLocation" : location.toString());
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

    private ArrayList findUnAskedPermissions(ArrayList<String> wanted) {
        ArrayList result = new ArrayList();

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

        return result;
    }

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

    private boolean canAskPermission() {
        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:
                Log.d(TAG, "onRequestPermissionsResult");
                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;
                        }
                    }
                } else {
                    Log.d(TAG, "No rejected permissions.");
                    canGetLocation = true;
                    getLocation();
                }
                break;
        }
    }

    public void showSettingsAlert() {
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
        alertDialog.setTitle("GPS is not Enabled!");
        alertDialog.setMessage("Do you want to turn on GPS?");
        alertDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);
            }
        });

        alertDialog.setNegativeButton("No", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        alertDialog.show();
    }

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

    private void updateUI(Location loc) {
        Log.d(TAG, "updateUI");
        tvLatitude.setText(Double.toString(loc.getLatitude()));
        tvLongitude.setText(Double.toString(loc.getLongitude()));
        tvTime.setText(DateFormat.getTimeInstance().format(loc.getTime()));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (locationManager != null) {
            locationManager.removeUpdates(this);
        }
    }
}

In the above code, we’re implementing runtime permissions that are used in Android 6.0+ devices. We’ve added the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions in the AndroidManifest.xml file.

Updating of location begin when Activity start. isProviderEnabled(String provider)method is called upon the locationManager object and is used to check whether GPS/Network Provider is enabled or not. If the Providers aren’t enabled we’re calling the method showSettingsAlert() that shows a prompt to enable GPS.

Method requestLocationUpdates() of the LocationManager class is used to register the service to be notified periodically by the named provider.

The parameters of this function are as follows:

  • provider is the name of the provider with which we would like to regiser.
  • minTime is minimum time interval between location updates (in milliseconds).
  • minDistance is minimum distance between location updates (in meters).
  • listener is a LocationListener whose onLocationChanged(Location) method will be called for each location update.

onLocationChanged is invoked periodically based upon the minTime and minDistance, whichever comes first.

onProviderDisabled will notify the app that GPS has been disabled on the device. Here we stop listening for updates when this happens.

onProviderEnabled lets us know that GPS is back and we can start listening again.

To stop location updates removeUpdates method is called on the LocationManager instance.

Result

android_location.png

LocationListener from gms.location

The Google Location Services API, part of Google Play Services, provides a more powerful, high-level framework that automatically handles location providers, user movement, and location accuracy. It also handles location update scheduling based on power consumption parameters you provide. In most cases, you’ll get better battery performance, as well as more appropriate accuracy, by using the Location Services API.

The main enhanced point is that Google Play Location Service provides Fused Location Provider. Main benefits of Fused Location Provider are:

  1. It provides simple and easy to use Location APIs.
  2. Provides high accuracy over other options.
  3. Utilizes low power by choosing the most efficient way to access the location.

With respect to Fused Location Provider, we can broadly classify the API usage in three use cases.

  • getLastLocation(GoogleApiClient) this API should be used when there is no need for continuous access to location from an application. Like one shot access or get user location based on some action. This is the simplified way to get the device location and also may not provide high accuracy.
  • requestLocationUpdates(GoogleApiClient,LocationRequest, LocationListener) this API should be used when there a need for continuous location updates and the location is accessed when the application is active in foreground.
  • requestLocationUpdates (GoogleApiClient, LocationRequest, PendingIntent) this API is used to receive location updates in the background even when the application is not active. So the difference is PendingIntent.

Google Play Services is required. If you are using Android Studio and Gradle, you should have the following dependencies added in build.gradle file

dependencies {
    ...
    // for emulator
    compile 'com.google.android.gms:play-services:8+'
    compile 'com.google.android.gms:play-services-location:8+'

    // for real device
    //compile 'com.google.android.gms:play-services:10.2.1'
}

Also specify the following required permission in your manifest.

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

Now you can access the last known location. The Fused Location Provider provides a new simple API. The following is an example activity which uses it.

The following interfaces should be implemented in Activity (for example) to get the location update.

  • GoogleApiClient.ConnectionCallbacks provides callbacks that are triggered when the client is connected (onConnected()) or temporarily disconnected (onConnectionSuspended()) from the service.
  • GoogleApiClient.OnConnectionFailedListener provides a callback method (onConnectionFailed()) that is triggered when an attempt to connect the client to the service results in a failure.
  • LocationListener defines the onLocationChanged() which is called when a user’s location changes. This method is only called if the LocationListener has been registered.

You can find out, if a GPS is enabled via the isProviderEnabled() method of LocationManager. If its not enabled you can send the user to the settings via an Intent with the Settings.ACTION_LOCATION_SOURCE_SETTINGS action for the android.provider.Settings class.

LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean enabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);

if (!enabled) {
  Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        startActivity(intent);
}

Fused Location Provider requires the GoogleApiClient instance to get the Location and it can be obtained as below.

gac = new GoogleApiClient.Builder(locationActivity)
    .addApi(LocationServices.API)
    .addConnectionCallbacks(this)
    .addOnConnectionFailedListener(this)
    .build();

Some explanation to code above:

  • Instantiating the GoogleApiClient should be done in onCreate of the application Activity.
  • gac.connect() should be done in onStart and gac.disconnect() in onStop.
  • When the GoogleApiClient is connected successfully, onConnected(Bundle) callback will be called. There we should register with LocationListener for location updates as LocationServices.FusedLocationApi.requestLocationUpdates( gac, locationRequest, this);

LocationRequest objects are used to request a quality of service for location updates from the FusedLocationProviderApi. For example, if your application wants high accuracy location it should create a location request with setPriority(int) set to PRIORITY_HIGH_ACCURACY and setInterval(long) to 5 seconds. This would be appropriate for mapping applications that are showing your location in real-time.

There are two options to set interval to receive updates:

  • setInterval() specifies the rate at which your app will like to receive updates.
  • setFastestInterval() specifies the fastest rate at which the app can handle updates. Setting the fastestInterval rate places a limit on how fast updates will be sent to your app.

The activity_main.xml layout is defined below.

<?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">

    <TextView
        android:text="Latitude:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvLatitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:text="Longitude:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvLongitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:text="Time of request:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/tvTime"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

The MainActivity.java class is defined below.

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

    final String TAG = "GPS";
    private long UPDATE_INTERVAL = 2 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 2000; /* 2 sec */
    static final int MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;

    GoogleApiClient gac;
    LocationRequest locationRequest;
    TextView tvLatitude, tvLongitude, tvTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvLatitude = (TextView) findViewById(R.id.tvLatitude);
        tvLongitude = (TextView) findViewById(R.id.tvLongitude);
        tvTime = (TextView) findViewById(R.id.tvTime);

        isGooglePlayServicesAvailable();

        if(!isLocationEnabled())
            showAlert();

        locationRequest = new LocationRequest();
        locationRequest.setInterval(UPDATE_INTERVAL);
        locationRequest.setFastestInterval(FASTEST_INTERVAL);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        gac = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    }

    @Override
    protected void onStart() {
        gac.connect();
        super.onStart();
    }

    @Override
    protected void onStop() {
        gac.disconnect();
        super.onStop();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            updateUI(location);
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

            return;
        }
        Log.d(TAG, "onConnected");

        Location ll = LocationServices.FusedLocationApi.getLastLocation(gac);
        Log.d(TAG, "LastLocation: " + (ll == null ? "NO LastLocation" : ll.toString()));

        LocationServices.FusedLocationApi.requestLocationUpdates(gac, locationRequest, this);
    }

    @Override
    public void onRequestPermissionsResult(
        int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this, "permission was granted, :)", Toast.LENGTH_LONG).show();

                    try{
                        LocationServices.FusedLocationApi.requestLocationUpdates(
                        gac, locationRequest, this);
                    } catch (SecurityException e) {
                        Toast.makeText(MainActivity.this, "SecurityException:\n" + e.toString(), Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(MainActivity.this, "permission denied, ...:(", Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

    @Override
    public void onConnectionSuspended(int i) {}

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Toast.makeText(MainActivity.this, "onConnectionFailed: \n" + connectionResult.toString(),
        Toast.LENGTH_LONG).show();
        Log.d("DDD", connectionResult.toString());
    }

    private void updateUI(Location loc) {
        Log.d(TAG, "updateUI");
        tvLatitude.setText(Double.toString(loc.getLatitude()));
        tvLongitude.setText(Double.toString(loc.getLongitude()));
        tvTime.setText(DateFormat.getTimeInstance().format(loc.getTime()));
    }

    private boolean isLocationEnabled() {
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
                locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

    private boolean isGooglePlayServicesAvailable() {
        final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
        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 {
                Log.d(TAG, "This device is not supported.");
                finish();
            }
            return false;
        }
        Log.d(TAG, "This device is supported.");
        return true;
    }

    private void showAlert() {
        final AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setTitle("Enable Location")
                .setMessage("Your Locations Settings is set to 'Off'.\nPlease Enable Location to " +
                        "use this app")
                .setPositiveButton("Location Settings", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {

                        Intent myIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        startActivity(myIntent);
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {

                    }
                });
        dialog.show();
    }
}

Result

android_gms_location.png
comments powered by Disqus