Introduction to Google Maps API for Android with Kotlin

In this Google Maps API Tutorial for Android you will learn how to retrieve the user’s current location, get location updates and search for places. By Joe Howard.

4.1 (17) · 1 Review

Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Receiving Location Updates

Knowing your user’s location at all times can help you provide a better experience. This section of the tutorial shows you how to continuously receive updates of your user’s location.

To do this, you first have to create a location request.

Open MapsActivity. Now add the following properties:

// 1
private lateinit var locationCallback: LocationCallback
// 2
private lateinit var locationRequest: LocationRequest
private var locationUpdateState = false

companion object {
  private const val LOCATION_PERMISSION_REQUEST_CODE = 1
  // 3
  private const val REQUEST_CHECK_SETTINGS = 2
}
  1. Declare a LocationCallback property.
  2. Declare a LocationRequest property and a location updated state property.
  3. REQUEST_CHECK_SETTINGS is used as the request code passed to onActivityResult.

Next add the following:

private fun startLocationUpdates() {
  //1
  if (ActivityCompat.checkSelfPermission(this,
      android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
        arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
        LOCATION_PERMISSION_REQUEST_CODE)
    return
  }
  //2
  fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null /* Looper */)
}
  1. In startLocationUpdates(), if the ACCESS_FINE_LOCATION permission has not been granted, request it now and return.
  2. If there is permission, request for location updates.

Now add the following method:

private fun createLocationRequest() {
  // 1
  locationRequest = LocationRequest()
  // 2
  locationRequest.interval = 10000
  // 3
  locationRequest.fastestInterval = 5000
  locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

  val builder = LocationSettingsRequest.Builder()
      .addLocationRequest(locationRequest)

  // 4
  val client = LocationServices.getSettingsClient(this)
  val task = client.checkLocationSettings(builder.build())

  // 5
  task.addOnSuccessListener {
    locationUpdateState = true
    startLocationUpdates()
  }
  task.addOnFailureListener { e ->
    // 6
    if (e is ResolvableApiException) {
      // Location settings are not satisfied, but this can be fixed
      // by showing the user a dialog.
      try {
        // Show the dialog by calling startResolutionForResult(),
        // and check the result in onActivityResult().
        e.startResolutionForResult(this@MapsActivity,
            REQUEST_CHECK_SETTINGS)
      } catch (sendEx: IntentSender.SendIntentException) {
        // Ignore the error.
      }
    }
  }
}

Here’s what’s going on in createLocationRequest():

Place Search

Where to Go From Here?

Before you start requesting for location updates, you need to check the state of the user’s location settings.

Now add the following three methods:

Here’s what’s going on:

Setup the LocationCallback() in onCreate() to be:

Here you update lastLocation with the new location and update the map with the new location coordinates.

Next, add a call to createLocationRequest() to the bottom of onCreate():

Your app is now set to receive location updates. When you change your location, the map will update with a new marker showing your new location. Note that the markers are still clickable to get the address as before.

Build and run. Play around with the app to view the changes:

Moving around

Since this app is supposed to be a guide, a user should be able to search for places of interest to them, right?

That’s where the Google Places API comes in; it provides your app the functionality to search for millions of institutions and places of interest. It’s Android Library provides a number of cool functionalities, one of them being the Place Picker, which is a UI widget that lets you provide a place search functionality with very few lines of code. Too good to be true? Try it!

Add the places API to your app build.gradle:

Once again, open MapsActivity.

Add this constant to the companion object:

Now add the following method:

This method creates a new builder for an intent to start the Place Picker UI and then starts the PlacePicker intent.

Now add the following lines of code to onActivityResult():

Here you retrieve details about the selected place if it has a RESULT_OK result for a PLACE_PICKER_REQUEST request, and then place a marker on that position on the map.

You are almost ready to try out the place search — you just need to call loadPlacePicker() inside the code.

You’ll create a floating action button (FAB) at the bottom-right of the map to trigger this method. FAB requires CoordinatorLayout which is part of the design support library.

Go back to build.gradle for the app and add the Android support design library as a dependency:

Then replace the contents of res > layout > activity_maps.xml with the following lines of code:

You were using a fragment element for map earlier; you’ve kept that and added a floating action button.

In MapsActivity, add the following lines of code to onCreate():

Build and run. Now when you click the search button at the bottom of the map the place picker will load:

Place Picker

You can download the final project from this tutorial here. Remember to put a valid Google Maps API key in google_maps_api.xml when running the final project.

This Google Maps API tutorial only brushed the surface of what you can do with the Google Maps APIs. The official Google documentation has much more about web services and and the Android API here.

You can also check out the developer page on other ways to customize the marker. User permission checks for run-time permissions need a better implementation than what you’ve done in this Google Maps API tutorial; the docs also have some great information about more advanced permission granting here.

Check out the developer pages for extensive reading on the Google Places API for Android, receiving location updates and mocking location data via the emulator’s extended controls.

If you have any questions or comments, please feel free to join the forum discussion below!

  1. You create an instance of LocationRequest, add it to an instance of LocationSettingsRequest.Builder and retrieve and handle any changes to be made based on the current state of the user’s location settings.
  2. interval specifies the rate at which your app will like to receive updates.
  3. fastestInterval 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.
  4. You create a settings client and a task to check location settings.
  5. A task success means all is well and you can go ahead and initiate a location request.
  6. A task failure means the location settings have some issues which can be fixed. This could be as a result of the user’s location settings turned off. You fix this by showing the user a dialog as shown below:
  7. // 1
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
      super.onActivityResult(requestCode, resultCode, data)
      if (requestCode == REQUEST_CHECK_SETTINGS) {
        if (resultCode == Activity.RESULT_OK) {
          locationUpdateState = true
          startLocationUpdates()
        }
      }
    }
    
    // 2
    override fun onPause() {
      super.onPause()
      fusedLocationClient.removeLocationUpdates(locationCallback)
    }
    
    // 3
    public override fun onResume() {
      super.onResume()
      if (!locationUpdateState) {
        startLocationUpdates()
      }
    }
    
    1. Override AppCompatActivity’s onActivityResult() method and start the update request if it has a RESULT_OK result for a REQUEST_CHECK_SETTINGS request.
    2. Override onPause() to stop location update request
    3. Override onResume() to restart the location update request.
    locationCallback = object : LocationCallback() {
      override fun onLocationResult(p0: LocationResult) {
        super.onLocationResult(p0)
    
        lastLocation = p0.lastLocation
        placeMarkerOnMap(LatLng(lastLocation.latitude, lastLocation.longitude))
      }
    }
    
    createLocationRequest()
    
    implementation 'com.google.android.gms:play-services-places:11.8.0'
    
    private const val PLACE_PICKER_REQUEST = 3
    
    private fun loadPlacePicker() {
      val builder = PlacePicker.IntentBuilder()
    
      try {
        startActivityForResult(builder.build(this@MapsActivity), PLACE_PICKER_REQUEST)
      } catch (e: GooglePlayServicesRepairableException) {
        e.printStackTrace()
      } catch (e: GooglePlayServicesNotAvailableException) {
        e.printStackTrace()
      }
    }
    
    if (requestCode == PLACE_PICKER_REQUEST) {
      if (resultCode == RESULT_OK) {
        val place = PlacePicker.getPlace(this, data)
        var addressText = place.name.toString()
        addressText += "\n" + place.address.toString()
    
        placeMarkerOnMap(place.latLng)
      }
    }
    
    implementation 'com.android.support:design:26.1.0'
    
    Note: As usual, if you are using a newer Android SDK version, you may need to update the version of this dependency as well so they match.
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:fitsSystemWindows="true">
    
      <fragment
        android:id="@+id/map"
        class="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
      <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="112dp"
        android:src="@android:drawable/ic_menu_search"/>
    
    </android.support.design.widget.CoordinatorLayout>
    
    val fab = findViewById<FloatingActionButton>(R.id.fab)
    fab.setOnClickListener {
      loadPlacePicker()
    }
    
  1. Override AppCompatActivity’s onActivityResult() method and start the update request if it has a RESULT_OK result for a REQUEST_CHECK_SETTINGS request.
  2. Override onPause() to stop location update request
  3. Override onResume() to restart the location update request.
Note: As usual, if you are using a newer Android SDK version, you may need to update the version of this dependency as well so they match.
// 1
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
  super.onActivityResult(requestCode, resultCode, data)
  if (requestCode == REQUEST_CHECK_SETTINGS) {
    if (resultCode == Activity.RESULT_OK) {
      locationUpdateState = true
      startLocationUpdates()
    }
  }
}

// 2
override fun onPause() {
  super.onPause()
  fusedLocationClient.removeLocationUpdates(locationCallback)
}

// 3
public override fun onResume() {
  super.onResume()
  if (!locationUpdateState) {
    startLocationUpdates()
  }
}
locationCallback = object : LocationCallback() {
  override fun onLocationResult(p0: LocationResult) {
    super.onLocationResult(p0)

    lastLocation = p0.lastLocation
    placeMarkerOnMap(LatLng(lastLocation.latitude, lastLocation.longitude))
  }
}
createLocationRequest()
implementation 'com.google.android.gms:play-services-places:11.8.0'
private const val PLACE_PICKER_REQUEST = 3
private fun loadPlacePicker() {
  val builder = PlacePicker.IntentBuilder()

  try {
    startActivityForResult(builder.build(this@MapsActivity), PLACE_PICKER_REQUEST)
  } catch (e: GooglePlayServicesRepairableException) {
    e.printStackTrace()
  } catch (e: GooglePlayServicesNotAvailableException) {
    e.printStackTrace()
  }
}
if (requestCode == PLACE_PICKER_REQUEST) {
  if (resultCode == RESULT_OK) {
    val place = PlacePicker.getPlace(this, data)
    var addressText = place.name.toString()
    addressText += "\n" + place.address.toString()

    placeMarkerOnMap(place.latLng)
  }
}
implementation 'com.android.support:design:26.1.0'
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true">

  <fragment
    android:id="@+id/map"
    class="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

  <android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_marginRight="8dp"
    android:layout_marginBottom="112dp"
    android:src="@android:drawable/ic_menu_search"/>

</android.support.design.widget.CoordinatorLayout>
val fab = findViewById<FloatingActionButton>(R.id.fab)
fab.setOnClickListener {
  loadPlacePicker()
}