Migrating to the New Places SDK Client

This guide explains the changes between the Places compatibility library and the new standalone version of the Places SDK for Android. If you have been using the Places compatibility library instead of migrating to the new standalone version of the Places SDK for Android, this guide shows you how to update your projects to use the new version of the Places SDK for Android.

The only way to access features and bug fixes in Places SDK for Android above Version 2.6.0 will be to use the Places SDK for Android. Google recommends updating from the compatibility library to the new Places SDK for Android version as soon as possible.

What's changed?

The main areas of change are as follows:

  • The new version of the Places SDK for Android is distributed as a static client library. Before January 2019, the Places SDK for Android was made available through Google Play services. Since then, a Places compatibility library was provided to ease the transition to the new Places SDK for Android.
  • There are all-new methods.
  • Field masks are now supported for methods that return place details. You can use field masks to specify which types of place data to return.
  • The status codes used to report errors have been improved.
  • Autocomplete now supports session tokens.
  • The Place Picker is no longer available.

About the Places compatibility library

In January 2019 with the release of Version 1.0 of the standalone Places SDK for Android, Google provided a compatibility library to help with the migration from the decommissioned Google Play Services version of the Places SDK for Android (com.google.android.gms:play-services-places).

This compatibility library was provided temporarily to redirect and translate API calls aimed at the Google Play Services version to the new standalone version until developers could migrate their code to use the new names in the standalone SDK. For each version of the Places SDK for Android that has been released from Version 1.0 through Version 2.6.0, a corresponding version of the Places compatibility library has been released to provide equivalent functionality.

Freezing and deprecating the Places compatibility library

All versions of the compatibility library for Places SDK for Android are deprecated as of March 31, 2022. Version 2.6.0 is the last version of the Places compatibility library. The only way to access features and bug fixes in Places SDK for Android above Version 2.6.0 will be to use the Places SDK for Android.

Google recommends that you migrate to the Places SDK for Android in order to access new features and critical bug fixes for releases above Version 2.6.0. If you are currently using the compatibility library, follow the steps below in the Install the Places SDK for Android section to migrate to the Places SDK for Android.

Install the client library

The new version of the Places SDK for Android is distributed as a static client library.

Use Maven to add the Places SDK for Android to your Android Studio project:

  1. If you are currently using the Places compatibility library:

    1. Replace the following line in the dependencies section:

          implementation 'com.google.android.libraries.places:places-compat:X.Y.Z'

      With this line to switch to the Places SDK for Android:

          implementation 'com.google.android.libraries.places:places:3.3.0'

  2. If you are currently using the Play Services version of Places SDK for Android:

    1. Replace the following line in the dependencies section:

          implementation 'com.google.android.gms:play-services-places:X.Y.Z'

      With this line to switch to the Places SDK for Android:

          implementation 'com.google.android.libraries.places:places:3.3.0'

  3. Sync your Gradle project.

  4. Set the minSdkVersion for your application project to 16 or higher.

  5. Update your "Powered by Google" assets:

    @drawable/powered_by_google_light // OLD
    @drawable/places_powered_by_google_light // NEW
    @drawable/powered_by_google_dark // OLD
    @drawable/places_powered_by_google_dark // NEW
    
  6. Build your app. If you see any build errors because of your conversion to the Places SDK for Android, see the sections below for information on resolving these errors.

Initialize the new Places SDK client

Initialize the new Places SDK client as shown in the following example:

// Add an import statement for the client library.
import com.google.android.libraries.places.api.Places;

...

// Initialize Places.
Places.initialize(getApplicationContext(), apiKey);

// Create a new Places client instance.
PlacesClient placesClient = Places.createClient(this);

Status codes

The status code for QPS limit errors has changed. QPS limit errors are now returned via PlaceStatusCodes.OVER_QUERY_LIMIT. There are no more QPD limits.

The following status codes have been added:

  • REQUEST_DENIED — The request was denied. Possible reasons for this include:

    • No API key was provided.
    • An invalid API key was provided.
    • The Places API has not been enabled in the Cloud console.
    • An API key was provided with incorrect key restrictions.
  • INVALID_REQUEST — The request is invalid due to a missing or invalid argument.

  • NOT_FOUND — No result was found for the given request.

New methods

The new version of the Places SDK for Android introduces all-new methods, which have been designed for consistency. All of the new methods adhere to the following:

  • Endpoints no longer use the get verb.
  • Request and response objects share the same name as the corresponding client method.
  • Request objects now have builders; required params are passed as request builder params.
  • Buffers are no longer used.

This section introduces the new methods, and shows you how they work.

Fetch a place by ID

Use fetchPlace() to get details about a particular place. fetchPlace() functions similarly to getPlaceById().

Follow these steps to fetch a place:

  1. Call fetchPlace(), passing a FetchPlaceRequest object specifying a Place ID and a list of fields specifying the Place data to return.

    // Define a Place ID.
    String placeId = "INSERT_PLACE_ID_HERE";
    
    // Specify the fields to return.
    List<Place.Field> placeFields = Arrays.asList(Place.Field.ID, Place.Field.DISPLAY_NAME);
    
    // Construct a request object, passing the place ID and fields array.
    FetchPlaceRequest request = FetchPlaceRequest.builder(placeId, placeFields)
            .build();
    
    
  2. Call addOnSuccessListener() to handle the FetchPlaceResponse. A single Place result is returned.

    // Add a listener to handle the response.
    placesClient.fetchPlace(request).addOnSuccessListener((response) -> {
      Place place = response.getPlace();
      Log.i(TAG, "Place found: " + place.getName());
    }).addOnFailureListener((exception) -> {
        if (exception instanceof ApiException) {
            ApiException apiException = (ApiException) exception;
            int statusCode = apiException.getStatusCode();
            // Handle error with given status code.
            Log.e(TAG, "Place not found: " + exception.getMessage());
        }
    });
    

Fetch a place photo

Use fetchPhoto() to get a place photo. fetchPhoto() returns photos for a place. The pattern for requesting a photo has been simplified. You can now request PhotoMetadata directly from the Place object; a separate request is no longer necessary. Photos can have a maximum width or height of 1600px. fetchPhoto() functions similarly to getPhoto().

Follow these steps to fetch place photos:

  1. Set up a call to fetchPlace(). Be sure to include the PHOTO_METADATAS field in your request:

    List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
    
  2. Get a Place object (this example uses fetchPlace(), but you can also use findCurrentPlace()):

    FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
    
  3. Add an OnSuccessListener to get the photo metadata from the resulting Place in the FetchPlaceResponse, then use the resulting photo metadata to get a bitmap and attribution text:

    placesClient.fetchPlace(placeRequest).addOnSuccessListener((response) -> {
        Place place = response.getPlace();
    
        // Get the photo metadata.
        PhotoMetadata photoMetadata = place.getPhotoMetadatas().get(0);
    
        // Get the attribution text.
        String attributions = photoMetadata.getAttributions();
    
        // Create a FetchPhotoRequest.
        FetchPhotoRequest photoRequest = FetchPhotoRequest.builder(photoMetadata)
                .setMaxWidth(500) // Optional.
                .setMaxHeight(300) // Optional.
                .build();
        placesClient.fetchPhoto(photoRequest).addOnSuccessListener((fetchPhotoResponse) -> {
            Bitmap bitmap = fetchPhotoResponse.getBitmap();
            imageView.setImageBitmap(bitmap);
        }).addOnFailureListener((exception) -> {
            if (exception instanceof ApiException) {
                ApiException apiException = (ApiException) exception;
                int statusCode = apiException.getStatusCode();
                // Handle error with given status code.
                Log.e(TAG, "Place not found: " + exception.getMessage());
            }
        });
    });
    

Find a place from the user's location

Use findCurrentPlace() to find the current location of the user's device. findCurrentPlace() returns a list of PlaceLikelihoods indicating places where the user's device is most likely to be located. findCurrentPlace() functions similarly to getCurrentPlace().

Follow these steps to get the current location of the user's device:

  1. Make sure your app requests the ACCESS_FINE_LOCATION and ACCESS_WIFI_STATE permissions. The user must grant permission to access their current device location. See Request App Permissions for details.

  2. Create a FindCurrentPlaceRequest, including a list of place data types to return.

      // Use fields to define the data types to return.
      List<Place.Field> placeFields = Arrays.asList(Place.Field.DISPLAY_NAME);
    
      // Use the builder to create a FindCurrentPlaceRequest.
      FindCurrentPlaceRequest request =
              FindCurrentPlaceRequest.builder(placeFields).build();
    
  3. Call findCurrentPlace and handle the response, checking first to verify that the user has granted permission to use their device location.

      // Call findCurrentPlace and handle the response (first check that the user has granted permission).
      if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
          placesClient.findCurrentPlace(request).addOnSuccessListener(((response) -> {
              for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                  Log.i(TAG, String.format("Place '%s' has likelihood: %f",
                          placeLikelihood.getPlace().getName(),
                          placeLikelihood.getLikelihood()));
                  textView.append(String.format("Place '%s' has likelihood: %f\n",
                          placeLikelihood.getPlace().getName(),
                          placeLikelihood.getLikelihood()));
              }
          })).addOnFailureListener((exception) -> {
              if (exception instanceof ApiException) {
                  ApiException apiException = (ApiException) exception;
                  Log.e(TAG, "Place not found: " + apiException.getStatusCode());
              }
          });
      } else {
          // A local method to request required permissions;
          // See https://developer.android.com/training/permissions/requesting
          getLocationPermission();
      }
    

Find autocomplete predictions

Use findAutocompletePredictions() to return place predictions in response to user search queries. findAutocompletePredictions() functions similarly to getAutocompletePredictions().

The following example shows calling findAutocompletePredictions():

// Create a new token for the autocomplete session. Pass this to FindAutocompletePredictionsRequest,
// and once again when the user makes a selection (for example when calling fetchPlace()).
AutocompleteSessionToken token = AutocompleteSessionToken.newInstance();
// Create a RectangularBounds object.
RectangularBounds bounds = RectangularBounds.newInstance(
  new LatLng(-33.880490, 151.184363),
  new LatLng(-33.858754, 151.229596));
// Use the builder to create a FindAutocompletePredictionsRequest.
FindAutocompletePredictionsRequest request = FindAutocompletePredictionsRequest.builder()
// Call either setLocationBias() OR setLocationRestriction().
   .setLocationBias(bounds)
   //.setLocationRestriction(bounds)
   .setCountry("au")
   .setTypesFilter(Arrays.asList(PlaceTypes.ADDRESS))
   .setSessionToken(token)
   .setQuery(query)
   .build();

placesClient.findAutocompletePredictions(request).addOnSuccessListener((response) -> {
   for (AutocompletePrediction prediction : response.getAutocompletePredictions()) {
       Log.i(TAG, prediction.getPlaceId());
       Log.i(TAG, prediction.getPrimaryText(null).toString());
   }
}).addOnFailureListener((exception) -> {
   if (exception instanceof ApiException) {
       ApiException apiException = (ApiException) exception;
       Log.e(TAG, "Place not found: " + apiException.getStatusCode());
   }
});

Session tokens

Session tokens group the query and selection phases of a user search into a discrete session for billing purposes. We recommend using session tokens for all autocomplete sessions. The session begins when the user starts typing a query, and concludes when they select a place. Each session can have multiple queries, followed by one place selection. Once a session has concluded, the token is no longer valid; your app must generate a fresh token for each session.

Field masks

In methods that return place details, you must specify which types of place data to return with each request. This helps to ensure that you only request (and pay for) data that you will actually use.

To specify which data types to return, pass an array of Place.Fields in your FetchPlaceRequest, as shown in the following example:

// Include address, ID, and phone number.
List<Place.Field> placeFields = Arrays.asList(Place.Field.FORMATTED_ADDRESS,
                                              Place.Field.ID,
                                              Place.Field.INTERNATIONAL_PHONE_NUMBER);

For a list of fields that you can use in a field mask, see Place Data Fields (New) .

Read more about Places Data SKUs.

Place Picker and Autocomplete updates

This section explains the changes to the Places widgets (Place Picker and Autocomplete).

Programmatic autocomplete

The following changes were made to autocomplete:

  • PlaceAutocomplete is renamed to Autocomplete.
    • PlaceAutocomplete.getPlace is renamed to Autocomplete.getPlaceFromIntent.
    • PlaceAutocomplete.getStatus is renamed to Autocomplete.getStatusFromIntent.
  • PlaceAutocomplete.RESULT_ERROR is renamed to AutocompleteActivity.RESULT_ERROR (error handling for the autocomplete fragment has NOT changed).

Place Picker

The Place Picker was deprecated on January 29, 2019. It was turned off on July 29, 2019, and is no longer available. Continued use will result in an error message. The new SDK does not support the Place Picker.

Autocomplete widgets

The autocomplete widgets have been updated:

  • The Place prefix has been removed from all classes.
  • Added support for session tokens. The widget manages tokens for you automatically in the background.
  • Added support for field masks, which let you choose which types of place data to return after the user makes a selection.

The following sections show how to add an autocomplete widget to your project.

Embed an AutocompleteFragment

To add an autocomplete fragment, take the following steps:

  1. Add a fragment to your activity's XML layout, as shown in the following example.

    <fragment
      android:id="@+id/autocomplete_fragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:name=
    "com.google.android.libraries.places.widget.AutocompleteSupportFragment"
      />
    
  2. To add the autocomplete widget to the activity, take these steps:

    • Initialize Places, passing the application context and your API key.
    • Initialize the AutocompleteSupportFragment.
    • Call setPlaceFields() to indicate the types of place data that you want to get.
    • Add a PlaceSelectionListener to do something with the result, as well as handle any errors that might occur.

    The following example shows adding an autocomplete widget to an activity:

    /**
     * Initialize Places. For simplicity, the API key is hard-coded. In a production
     * environment we recommend using a secure mechanism to manage API keys.
     */
    if (!Places.isInitialized()) {
        Places.initialize(getApplicationContext(), "YOUR_API_KEY");
    }
    
    // Initialize the AutocompleteSupportFragment.
    AutocompleteSupportFragment autocompleteFragment = (AutocompleteSupportFragment)
            getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);
    
    autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.DISPLAY_NAME));
    
    autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
        @Override
        public void onPlaceSelected(Place place) {
            // TODO: Get info about the selected place.
            Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());
        }
    
        @Override
        public void onError(Status status) {
            // TODO: Handle the error.
            Log.i(TAG, "An error occurred: " + status);
        }
    });
    

Use an intent to launch the autocomplete activity

  1. Initialize Places, passing app context and your API key
  2. Use Autocomplete.IntentBuilder to create an intent, passing the desired PlaceAutocomplete mode (full-screen, or overlay). The intent must call startActivityForResult, passing in a request code that identifies your intent.
  3. Override the onActivityResult callback to receive the selected place.

The following example shows you how to use an intent to launch autocomplete, and then handle the result:

    /**
     * Initialize Places. For simplicity, the API key is hard-coded. In a production
     * environment we recommend using a secure mechanism to manage API keys.
     */
    if (!Places.isInitialized()) {
        Places.initialize(getApplicationContext(), "YOUR_API_KEY");
    }

    ...

    // Set the fields to specify which types of place data to return.
    List<Place.Field> fields = Arrays.asList(Place.Field.ID, Place.Field.DISPLAY_NAME);

    // Start the autocomplete intent.
    Intent intent = new Autocomplete.IntentBuilder(
            AutocompleteActivityMode.FULLSCREEN, fields)
            .build(this);
    startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE);

    ...

    /**
     * Override the activity's onActivityResult(), check the request code, and
     * do something with the returned place data (in this example its place name and place ID).
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                Place place = Autocomplete.getPlaceFromIntent(data);
                Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());
            } else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
                // TODO: Handle the error.
                Status status = Autocomplete.getStatusFromIntent(data);
                Log.i(TAG, status.getStatusMessage());
            } else if (resultCode == RESULT_CANCELED) {
                // The user canceled the operation.
            }
        }
    }

Place Picker is no longer available

The Place Picker was deprecated on January 29, 2019. It was turned off on July 29, 2019, and is no longer available. Continued use will result in an error message. The new SDK does not support the Place Picker.