How to get User Location in Android

How to get User Location in Android

Many apps in Android require user location access for better functionality. For example- Ola, Uber, etc for ordering cabs or Swiggy, Zomato, etc for delivering foods and many more.

Fetching a User's location in Android can be done through the following steps

1. Adding dependency in Gradle

Add the following dependency in your build.gradle(app) file:

dependencies {
    implementation 'com.google.android.gms:play-services-location:19.0.0'
}

2. Acquiring user permissions

Any of the following permissions can be used-
(Add these permissions in the Android Manifest.xml file)

ACCESS_COARSE_LOCATION: It provides location accuracy within a city block

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

ACCESS_FINE_LOCATION: It provides a more accurate location

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

ACCESS_BACKGROUND_LOCATION: In case the app needs to access the user’s location while the app is running in the background, we need to add this permission along with the above ones

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

📌 Accuracy

Android supports the following levels of location accuracy:

- Approximate
Provides an estimate of the device's location, within about 1 mile (1.6 km). Your app uses this level of location accuracy when you declare the ACCESS_COARSE_LOCATION permission but not the ACCESS_FINE_LOCATION permission.

- Precise
Provides an estimate of the device's location that is as accurate as possible, usually within about 160 feet (50 meters) and sometimes as accurate as within 10 feet (a few meters) or better. Your app uses this level of location accuracy when you declare the ACCESS_FINE_LOCATION permission.

Note:

On Android 12 (API level 31) or higher, users can request that your app retrieve only approximate location information, even when your app requests the ACCESS_FINE_LOCATION runtime permission.

To handle this potential user behavior, don't request the ACCESS_FINE_LOCATION permission by itself. Instead, request both the ACCESS_FINE_LOCATION permission and the ACCESS_COARSE_LOCATION permission in a single runtime request. If you try to request only ACCESS_FINE_LOCATION, the system ignores the request on some releases of Android 12.

If your app targets Android 12 or higher, the system logs the following error message in Logcat:

ACCESS_FINE_LOCATION must be requested with ACCESS_COARSE_LOCATION

When your app requests both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION, the system permissions dialog includes the following options for the user:

Precise: This allows your app to get precise location information.
Approximate: This allows your app to get only approximate location information.

3. Designing the layout

Create a TextView to display the user's latitude and longitude on the screen

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Location here"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

4. Logic Writing

We write the logic in our main Activity file. In my case it is MainActivity.java.

Create some basic variables -
Location object to store the user's location,
FusedLocationProviderClient object to fetch location, an integer request code for permissions, and TextView object to show the fetched location to the user

Also creating a fetchLocation() function for fetching the location.

In order to get the last location of the user, make use of the Java public class FusedLocationProviderClient.
It is actually a location service that combines GPS location and network location to achieve a balance between battery consumption and accuracy. GPS location is used to provide accuracy and network location is used to get location when the user is indoors.

public class MainActivity extends AppCompatActivity {
    Location currentLocation;
    FusedLocationProviderClient fusedLocationProviderClient;
    private static final int REQUEST_CODE = 101;
    TextView textView;

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
        fetchLocation();
    }
}

The permissions required are -

  • Manifest.permission.ACCESS_COARSE_LOCATION
  • Manifest.permission.ACCESS_FINE_LOCATION
String[] LocationPermissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                                        Manifest.permission.ACCESS_FINE_LOCATION};

The logic for the fetchLocation() can be as follows -

  1. Check if the permissions we request are enabled.
  2. If not enabled, request the permissions.
  3. If permissions are accepted and location enabled, get the last location of the user
@RequiresApi(api = Build.VERSION_CODES.N)
private void fetchLocation() {
        // Check for permissions
        if (ActivityCompat.checkSelfPermission
                (this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
            return;
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
        }

        // Getting last location from Fused Location Provider Client Object
        fusedLocationProviderClient.getLastLocation().
                addOnCompleteListener(new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        Location location = task.getResult();
                        if (location == null) {
                            requestNewLocationData();
                        } else {
                            currentLocation = location;
                            textView.setText("Latitude:"+currentLocation.getLatitude()+"\nLongitude:"+currentLocation.getLongitude());
                        }
                    }
                });
 }

OnRequestPermissionResult :

@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                fetchLocation();
            }
            else {
                // Permission not granted
                Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
                currentLocation=null;
            }
        }
 }

As you can see above in the fetchLocation method, if the location fetched by fusedLocationProviderClient.getLastLocation() is null we request for new location data and hence create a method named requestNewLocationData()

fusedLocationProviderClient.getLastLocation().
        addOnCompleteListener(new OnCompleteListener<Location>() {
            @Override
            public void onComplete(@NonNull Task<Location> task) {
                Location location = task.getResult();
                if (location == null) {
                    requestNewLocationData(); // Here
                } else {
                   // .....
                }
            }

RequestNewLocationData()

Going to requestNewLocationData() method now.

🚩 Creating a variable of the class LocationCallback (Used for receiving notifications from the FusedLocationProviderApi when the device location has changed or can no longer be determined)

private LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            Location location=locationResult.getLastLocation();
            currentLocation=location;
        }
    };

🚩 Creating a new LocationRequest variable. On this object, set a variety of methods such as set the priority of how accurate the location to be or in how many intervals, request of the location is to be made.

🚩If very high accuracy is required, use PRIORITY_HIGH_ACCURACY as an argument to the setPriority(int) method. For a city-level accuracy(low accuracy), use PRIORITY_LOW_POWER.

🚩Once the LocationRequest object is ready, set it on the FusedLocationProviderClient object to get the final location

private void requestNewLocationData() {
        // Initializing LocationRequest object with appropriate methods
        LocationRequest locationRequest = new LocationRequest();

        //  For a city level accuracy(low accuracy), use PRIORITY_LOW_POWER.
        locationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);

        // Set the desired interval for active location updates, in milliseconds.
        locationRequest.setInterval(5);

        // Explicitly set the fastest interval for location updates, in milliseconds
        locationRequest.setFastestInterval(0);

        // Set the number of location updates.
        locationRequest.setNumUpdates(1);

        // setting LocationRequest on FusedLocationClient
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);

        // Check for permissions
        if (ActivityCompat.checkSelfPermission
                (this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
            return;
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
        }

        fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
}

Now you have successfully fetched the user's location and can implement this in your app as required.

Complete code

The complete MainActivity.java(in my case) looks like this -

package com.example.locationdemo;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;

public class MainActivity extends AppCompatActivity {
    Location currentLocation;
    FusedLocationProviderClient fusedLocationProviderClient;
    private static final int REQUEST_CODE = 101;
    TextView textView;

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
        fetchLocation();
    }

    String[] LocationPermissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                                        Manifest.permission.ACCESS_FINE_LOCATION};

    @RequiresApi(api = Build.VERSION_CODES.N)
    private void fetchLocation() {
        // Check for permissions
        if (ActivityCompat.checkSelfPermission
                (this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
            return;
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
        }

        // Getting last location from Fused Location Provider Client Object
        fusedLocationProviderClient.getLastLocation().
                addOnCompleteListener(new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        Location location = task.getResult();
                        if (location == null) {
                            requestNewLocationData();
                        } else {
                            currentLocation = location;
                            textView.setText("Latitude:"+currentLocation.getLatitude()+"\nLongitude:"+currentLocation.getLongitude());
                        }
                    }
                });
    }

    private LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            Location location=locationResult.getLastLocation();
            currentLocation=location;
        }
    };

    private void requestNewLocationData() {
        // Initializing LocationRequest object with appropriate methods
        LocationRequest locationRequest = new LocationRequest();

        //  For a city level accuracy(low accuracy), use PRIORITY_LOW_POWER.
        locationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);

        // Set the desired interval for active location updates, in milliseconds.
        locationRequest.setInterval(5);

        // Explicitly set the fastest interval for location updates, in milliseconds
        locationRequest.setFastestInterval(0);

        // Set the number of location updates.
        locationRequest.setNumUpdates(1);

        // setting LocationRequest on FusedLocationClient
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);

        // Check for permissions
        if (ActivityCompat.checkSelfPermission
                (this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
            return;
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
            // Request for permissions
            ActivityCompat.requestPermissions(
                    this, LocationPermissions, REQUEST_CODE);
        }

        fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                fetchLocation();
            }
            else {
                // Permission not granted
                Toast.makeText(this, "Location cannot be determined", Toast.LENGTH_SHORT).show();
                currentLocation=null;
            }
        }
    }
}

Conclusion

This is the end of the blog. Make sure to give a follow if you liked this.
Thanks💛