Using GPS, Location Manager in Android: Android Tutorial

After booking an Uber cab, have you noticed the movement of cabs on the roads while coming to your address? There are various applications that use some kind of location services. Using GPS to update the location is a very cool feature. That moving icon of a car(in case of Uber) looks very cool and being an Android developer, we all have thought of using these type of features in our mobile application but ended up with some kind of error while integrating this feature.

So, don’t worry, in this blog we will learn how to use Fused Location API to get the accurate location of your Android device by making use of your phone’s own GPS. We will show the current location of the mobile using the GPS and whenever the location of the phone will be updated then the location will be updated on the Map also. So, let’s get started.

Before moving forward

Before moving forward to the coding part, we need to understand various things that are related to the Google Map. So, let’s understand them first.

Location Permissions

If you want to get the current location of your user, then you have to add the location permission to your application. Google map provides two ways of getting the current location of the user:

  1. android.permission.ACCESS_COARSE_LOCATION — By adding this in your application, allows you to use WIFI or mobile cell data(or both) to determine the device’s location. The approximation by using this permission is close to the city level.
  2. android.permission.ACCESS_FINE_LOCATION — This uses your GPS as well as WIFI and mobile data to get the most precise location as possible. By using this, you will get a precise location of your user.

Map type

Have you ever noticed or seen the types of maps that Google provides to its user? By default, you have seen the NORMAL map type, which shows the street view in the map. But apart from this, Google map offers various other map types that you can use in our application:

  1. MAP_TYPE_NORMAL: This is used to have a road or street view on the map. This is generally seen in most of the application that uses the Google Map. It shows the street name along with the details of the shops present on that street or some other place.
  2. MAP_TYPE_SATELLITE: This shows the satellite view of an Area. This does not include any street name and labels.
  3. MAP_TYPE_TERRAIN: This shows the terrain view of any area. It includes various colors, counter lines, and shading to display the exact view on an area.
  4. MAP_TYPE_HYBRID: It uses both satellite and normal view i.e. the map displayed will be of satellite view but this type the name of streets will also be visible.
  5. MAP_TYPE_NONE: This is a normal map and doesn’t display any labels on the map.

Google Map API key

In order to use a map feature in your Android application, you need to have an API key that is generated by Google to use it in your application. Without having the API key, you will not able to use the Maps feature. So, let’s create an API key for Google Map.

Step 1: Go to the Google Developer Console and sign in with your Gmail account. You will get an interface something like this:

Step 2: Now create a new project by clicking on Select a project and after that click on New Project

Step 3: Enter the details of your project and click on Create.

Step 4: After creating a project, go to APIs and Services and then click on Library.

Step 5: Search for “maps SDK for android” and click on the first option available.

Step 6: At last, enable the Maps SDK for Android by clicking on Enable button.

Step 7: A dashboard similar to below will be shown to you, click on Credentials and then on Create credentials and then click on API key

Step 8: A popup will be showed with your API key. Copy the API key and keep it. We will use this API key later in this blog.

So, till now, we are done with the introduction part of the Google map. Now, let’s move on to one example.

Example of Google Maps using Fused Location

In this example, we will use Fused Location API to get the changed location of an user. Using Fused Location is very simple and it is very battery efficient also. Also, we will be using LocationRequest that are used to request a quality of service for location updates from the FusedLocationProviderApi.

LocationRequest includes various methods for using a map like a pro. Some of the methods that we will use in this blog are:

  • setInterval(long millis): This is used to set the desired interval after which you want to check for a location update. It is in milliseconds.
  • setFastestInterval(long millis): This is used to set the fastest interval for a location update.
  • setSmallestDisplacement(float smallestDisplacementMeters): This will set the minimum displacement between location updates. It is in meters and the default value of this is 0.
  • setPriority(int priority): This is used to set the priority of location received. It can be PRIORITY_BALANCED_POWER_ACCURACY(for an accuracy up to block level) or it can be PRIORITY_HIGH_ACCURACY(to get the most accurate result) or it can be PRIORITY_LOW_POWER(to get an accuracy up to city level) or it can be PRIORITY_NO_POWER(to get the most accurate information without providing some additional power).

Following are the steps involved in using GPS tracking for location updates in your app:

Step 1: The very first step is to go to the Google Developer Console and create an API key for “Maps SDK for Android”. You can find the steps involved in API creation at the beginning of this blog.

Step 2: After creating the API key, go to Android Studio and create a new project with Empty Activity.

Step 3: Next step is to add some dependencies and permission to your app. In order to use Fused Location, you have to add the dependency of location play service. So, go to your build.gradle and the below dependency:

implementation 'com.google.android.gms:play-services-location:16.0.0'
implementation 'com.google.android.gms:play-services-maps:16.0.0'

After adding the dependency, add the permission to access fine location and internet permission by adding the below line in your AndroidManifest.xml file:

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

Step 4: Now add the API key in your application. Go to AndroidManifest.xml and add the below line under the application tag:

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="PASTE_YOUR_API_KEY_HERE"/>

So, we are done with the dependencies and permissions part. Now let’s move on to the UI part.

Step 5: Now go to the activity_main.xml and add a fragment to include map fragment in your application. Add the below code:

<fragment 
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

We are done with adding the map in our xml file.

Step 6: The last step is to add the code for location change in our MainActivity.kt file. Add the below code in your file:

class MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
    LocationListener{
    private lateinit var mGoogleMap: GoogleMap
    private lateinit var mapFrag: SupportMapFragment
    private val MY_PERMISSIONS_REQUEST_LOCATION = 99
    private lateinit var mLocationRequest: LocationRequest
    private lateinit var mGoogleApiClient:GoogleApiClient
    private lateinit var mLastLocation: Location
    private lateinit var mCurrLocationMarker: Marker

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            checkLocationPermission()
        }
        mapFrag = getSupportFragmentManager().findFragmentById(R.id.map) as SupportMapFragment
        mapFrag.getMapAsync(this)
    }
    override fun onPause() {
        super.onPause()
        //stop location updates when Activity is no longer active
        if (mGoogleApiClient != null){
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this)
        }
    }
    override fun onResume() {
        super.onResume()

        if ((mGoogleApiClient != null && (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED))){
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this)
        }
    }
    override fun onMapReady(googleMap:GoogleMap) {
        mGoogleMap = googleMap
        mGoogleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL)
        //Initialize Google Play Services

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if ((ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED)){
                buildGoogleApiClient()
                mGoogleMap.setMyLocationEnabled(true)
                mGoogleMap.setMinZoomPreference(15F)
            }
        }else{
            buildGoogleApiClient()
            mGoogleMap.setMyLocationEnabled(true)
            mGoogleMap.setMinZoomPreference(15F)
        }
    }
    @Synchronized protected fun buildGoogleApiClient() {
        mGoogleApiClient = GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()
        mGoogleApiClient.connect()
    }

    override fun onConnected(p0: Bundle?) {
        mLocationRequest = LocationRequest()
        mLocationRequest.setInterval(2000)
        mLocationRequest.setFastestInterval(2000)
        mLocationRequest.setSmallestDisplacement(5.0f)
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
        if ((ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED)){
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this)
        }
    }

    override fun onConnectionSuspended(i:Int) {}
    override fun onConnectionFailed(connectionResult: ConnectionResult) {}
    override fun onLocationChanged(location:Location) {
        Toast.makeText(this, ("Location Changed " + location.getLatitude()
                + " " + location.getLongitude()), Toast.LENGTH_SHORT).show()
        mLastLocation = location
        if (mCurrLocationMarker != null){
            mCurrLocationMarker.remove()
        }
        //Place current location marker
        val latLng = LatLng(location.getLatitude(), location.getLongitude())
        val markerOptions = MarkerOptions()
        markerOptions.position(latLng)
        markerOptions.title("Current Position")
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))
        mCurrLocationMarker = mGoogleMap.addMarker(markerOptions)
        //move map camera
        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng))
        mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(15F))
    }
    fun checkLocationPermission():Boolean {
        if ((ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) !== PackageManager.PERMISSION_GRANTED)){
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)){
                //Prompt the user once explanation has been shown
                ActivityCompat.requestPermissions(this,
                    arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
                    MY_PERMISSIONS_REQUEST_LOCATION)
            }
            else{
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                    arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
                    MY_PERMISSIONS_REQUEST_LOCATION)
            }
            return false
        } else {
            return true
        }
    }
    override fun onRequestPermissionsResult(requestCode:Int,
                                            permissions:Array<String>, grantResults:IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_LOCATION -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)){
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                    if ((ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION) === PackageManager.PERMISSION_GRANTED)){
                        if (mGoogleApiClient == null){
                            buildGoogleApiClient()
                        }
                        mGoogleMap.setMyLocationEnabled(true)
                    }
                } else {
                    // permission denied, Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
                }
                return
            }
        }
        // other 'case' lines to check for other
        // permissions this app might request
    }
}

Let’s understand the above code. If you want to use the Google map service(access fine location) then you have to take permission from the user of the app, at least one time. So, our very first step is to check if the permission is granted or not. If not then you have to display an alert box to demand permission, something like this:

If the permission is granted then the onMapReady() method will be called and the current location of the user will be displayed on the map.

There is a function called onConnected() that will refresh the location in every 2 seconds or on every change in 5 meters.

In order to set the marker to the new position, we have one function called onLocationChanged() that will place the marker to the new position.

Other methods such as onPause() and onResume() are there to disconnect the location service when the application is in pause state and again reconnect the location service of the application comes in the foreground state, respectively.

Run your app in your mobile and you will see your current location in the app. To have a more clear understanding, try to run your app while traveling in a bus or car. You will see your marker moving in the same way as in the Uber app. All you need to do is remove my marker with your own. You can add some bus marker or car marker or anything of your choice.

Summary

In this blog, we learned how to display the changed location of the user in your app. We have used Fused Location API for the same purpose. Firstly, we look at some important concepts of Google maps along with the steps required to make an API key and at the last we did one example in which we place a marker at our current location on the map and that marker will be moved when your location gets changed. To have a clear understanding, use the above code and try to do experiment with the code.

Keep Learning :)

Team MindOrks!