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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to Google Maps API for Android with Kotlin
30 mins
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
}
- Declare a LocationCallback property.
- Declare a LocationRequest property and a location updated state property.
-
REQUEST_CHECK_SETTINGS
is used as the request code passed toonActivityResult
.
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 */)
}
- In startLocationUpdates(), if the ACCESS_FINE_LOCATION permission has not been granted, request it now and return.
- 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:
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:
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!
- 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.
- interval specifies the rate at which your app will like to receive updates.
-
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. - You create a settings client and a task to check location settings.
- A task success means all is well and you can go ahead and initiate a location request.
- 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:
- Override AppCompatActivity’s onActivityResult() method and start the update request if it has a RESULT_OK result for a REQUEST_CHECK_SETTINGS request.
- Override onPause() to stop location update request
- Override onResume() to restart the location update request.
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:
// 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()
}
}
Here’s what’s going on:
Setup the LocationCallback() in onCreate() to be:
locationCallback = object : LocationCallback() {
override fun onLocationResult(p0: LocationResult) {
super.onLocationResult(p0)
lastLocation = p0.lastLocation
placeMarkerOnMap(LatLng(lastLocation.latitude, lastLocation.longitude))
}
}
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():
createLocationRequest()
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:
Place Search
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:
implementation 'com.google.android.gms:play-services-places:11.8.0'
Once again, open MapsActivity.
Add this constant to the companion object:
private const val PLACE_PICKER_REQUEST = 3
Now add the following method:
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()
}
}
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():
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)
}
}
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:
implementation 'com.android.support:design:26.1.0'
Then replace the contents of res > layout > activity_maps.xml with the following lines of code:
<?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>
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():
val fab = findViewById<FloatingActionButton>(R.id.fab)
fab.setOnClickListener {
loadPlacePicker()
}
Build and run. Now when you click the search button at the bottom of the map the place picker will load:
Where to Go From Here?
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!
- Override AppCompatActivity’s onActivityResult() method and start the update request if it has a RESULT_OK result for a REQUEST_CHECK_SETTINGS request.
- Override onPause() to stop location update request
- Override onResume() to restart the location update request.
// 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()
}