Android Google Places Autocomplete (Places SDK 2.0 for Android)

October 9, 2019

Google Cloud Console Setup

Enable Maps SDK for Android on Google Cloud Console

Enable Billing

Get API Key

Goto Google Cloud Console -> Credentials

  • Create credentials -> API key (copy API Key)
  • Click Restrict Key
    • name: Android-GoogleCloud
    • Application restrictions: Android apps; Restrict usage to your Android apps: Add Item (Package Name, Sha1)
    • API restrictions: Restrict key for Maps SDK for Android, Places API

Android Setup

Edit Module:app build.gradle

dependencies {
  implementation 'com.google.android.libraries.places:places:2.0.0'
}

Initialize at Application class.

val API_KEY = ...

Places.initialize(getApplicationContext(), API_KEY)

NOTE: Refer Android How to Secure Api Key

Autocomplete Activity

Popup an activity to allow user to search for places and select a place.

class CardFragment : Fragment() {

    companion object {
        const val REQUEST_PLACES_AUTOCOMPLETE = 1
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        // super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == REQUEST_PLACES_AUTOCOMPLETE) {
            when (resultCode) {
                RESULT_OK -> {
                    val place = Autocomplete.getPlaceFromIntent(data!!)

                    Timber.d("place=${place.id}, ${place.name}, ${place.latLng}, ${place.types}")
                }
                AutocompleteActivity.RESULT_ERROR -> {
                    // TODO: Handle the error.
                    val status = Autocomplete.getStatusFromIntent(data!!)
                    Timber.e("status=$status")
                }
                RESULT_CANCELED -> // The user canceled the operation.
                    Timber.d("cancelled")
            }
        }
    }

    private fun launchAutocomplete() {
        val fields = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS_COMPONENTS, Place.Field.TYPES)
        val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.OVERLAY, fields)
            // .setLocationBias(bounds)
            .build(context)
        startActivityForResult(intent, REQUEST_PLACES_AUTOCOMPLETE)
    }
}

NOTE: Refer to Build RectangularBounds From a List of LatLng for setLocationBias(bounds)

Autocomplete programmatically

val placesClient = Places.createClient(context)

val token = AutocompleteSessionToken.newInstance()
// val bounds = RectangularBounds.newInstance(latLng, latLng)


val request = FindAutocompletePredictionsRequest.builder()
    // .setLocationBias(bounds)
    .setSessionToken(token)
    .setTypeFilter(TypeFilter.ESTABLISHMENT)
    .setCountry("US")
    .setQuery("Bahn Mi Cafe")
    .build()
placesClient.findAutocompletePredictions(request)

val response = placesClient.findAutocompletePredictions(request).await()
for (prediction in response.autocompletePredictions) {
    Timber.d("prediction=${prediction.placeId}, ${prediction.getPrimaryText(null)}")
}

NOTE: For Task.await(), refer to Google Api Get Task Result Without Callback

Get Place Detail

val fields = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS_COMPONENTS, Place.Field.TYPES)

// val request = FetchPlaceRequest.newInstance(prediction.placeId, fields)
val request = FetchPlaceRequest.builder(prediction.placeId, fields)
    .setSessionToken(token)
    .build()
placesClient.fetchPlace(request)

val placeResponse = placesClient.fetchPlace(request).await()
val place = placeResponse.place
Timber.d("place=${place.id }, ${place.name}")

Troubleshoot

If you bump into the following error

  • com.google.android.gms.common.api.ApiException: 9011: This API project is not authorized to use this API.
  • Places: Error while autocompleting: REQUEST_DENIED

Map sure you enable APIs for Maps SDK for Android and Places API and API Key Restriction includes them as well.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.