Como migrar para o novo cliente do SDK do Places

Este guia explica as mudanças entre a biblioteca de compatibilidade do Places e a nova versão independente do SDK do Places para Android. Se você estiver usando a biblioteca de compatibilidade do Places em vez de migrar para a nova versão independente do SDK do Places para Android, este guia mostra como atualizar seus projetos para usar a nova versão do SDK do Places para Android.

A única maneira de acessar os recursos e as correções de bugs no SDK do Places para Android versão 2.6.0 e posteriores é usar o SDK do Places para Android. O Google recomenda atualizar da biblioteca de compatibilidade para a nova versão do SDK do Places para Android o mais rápido possível.

O que mudou?

As principais áreas de mudança são as seguintes:

  • A nova versão do SDK do Places para Android é distribuída como uma biblioteca de cliente estática. Antes de janeiro de 2019, o SDK do Places para Android era disponibilizado pelo Google Play Services. Desde então, uma biblioteca de compatibilidade do Places foi fornecida para facilitar a transição para o novo SDK do Places para Android.
  • métodos totalmente novos.
  • Máscaras de campo agora são compatíveis com métodos que retornam detalhes do lugar. É possível usar máscaras de campo para especificar quais tipos de dados de lugar serão retornados.
  • Os códigos de status usados para informar erros foram aprimorados.
  • O preenchimento automático agora aceita tokens de sessão.
  • O Place Picker não está mais disponível.

Sobre a biblioteca de compatibilidade do Places

Em janeiro de 2019, com o lançamento da versão 1.0 do SDK independente do Places para Android, o Google forneceu uma biblioteca de compatibilidade para ajudar na migração da versão desativada do SDK do Places para Android do Google Play Services (com.google.android.gms:play-services-places).

Essa biblioteca de compatibilidade foi fornecida temporariamente para redirecionar e traduzir chamadas de API destinadas à versão do Google Play Services para a nova versão independente até que os desenvolvedores pudessem migrar o código para usar os novos nomes no SDK independente. Para cada versão do SDK do Places para Android lançada da versão 1.0 à 2.6.0, uma versão correspondente da biblioteca de compatibilidade do Places foi lançada para oferecer funcionalidade equivalente.

Congelamento e descontinuação da biblioteca de compatibilidade do Places

Todas as versões da biblioteca de compatibilidade do SDK do Places para Android foram descontinuadas em 31 de março de 2022. A versão 2.6.0 é a última versão da biblioteca de compatibilidade do Places. A única maneira de acessar os recursos e as correções de bugs no SDK do Places para Android versão 2.6.0 e posteriores é usar o SDK do Places para Android.

O Google recomenda migrar para o SDK do Places para Android para acessar novos recursos e correções de bugs críticas para versões acima da 2.6.0. Se você estiver usando a biblioteca de compatibilidade, siga as etapas abaixo na seção Instalar o SDK do Places para Android para migrar para o SDK do Places para Android.

Instale a biblioteca de cliente

A nova versão do SDK do Places para Android é distribuída como uma biblioteca de cliente estática.

Use o Maven para adicionar o Places SDK para Android ao seu projeto do Android Studio:

  1. Se você estiver usando a biblioteca de compatibilidade do Places:

    1. Substitua a seguinte linha na seção dependencies:

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

      Com esta linha para mudar para o SDK do Places para Android:

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

  2. Se você estiver usando a versão do Play Services do SDK do Places para Android:

    1. Substitua a seguinte linha na seção dependencies:

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

      Com esta linha para mudar para o SDK do Places para Android:

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

  3. Sincronize seu projeto do Gradle.

  4. Defina a minSdkVersion do projeto do aplicativo como 16 ou mais recente.

  5. Atualize os recursos "Com tecnologia do Google":

    @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. Crie seu app. Se você encontrar erros de build devido à conversão para o SDK do Places para Android, consulte as seções abaixo para saber como resolver esses erros.

Inicializar o novo cliente do SDK do Places

Inicialize o novo cliente do SDK do Places, conforme mostrado no exemplo a seguir:

// 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);

Códigos de status

O código de status dos erros de limite de QPS foi alterado. Os erros de limite de QPS agora são retornados por PlaceStatusCodes.OVER_QUERY_LIMIT. Não há mais limites de QPD.

Os seguintes códigos de status foram adicionados:

  • REQUEST_DENIED: a solicitação foi negada. Alguns possíveis motivos para isso:

    • Nenhuma chave de API foi fornecida.
    • Uma chave de API inválida foi fornecida.
    • A API Places não foi ativada no console do Cloud.
    • Uma chave de API foi fornecida com restrições incorretas.
  • INVALID_REQUEST: a solicitação é inválida devido a um argumento ausente ou inválido.

  • NOT_FOUND: nenhum resultado foi encontrado para a solicitação.

Novos métodos

A nova versão do SDK do Places para Android apresenta métodos totalmente novos, que foram projetados para consistência. Todos os novos métodos aderem ao seguinte:

  • Os endpoints não usam mais o verbo get.
  • Os objetos de solicitação e resposta compartilham o mesmo nome do método do cliente correspondente.
  • Os objetos de solicitação agora têm builders. Os parâmetros obrigatórios são transmitidos como parâmetros do builder de solicitação.
  • Os buffers não são mais usados.

Esta seção apresenta os novos métodos e mostra como eles funcionam.

Buscar um lugar por ID

Use fetchPlace() para conferir detalhes sobre um lugar específico. fetchPlace() funciona de maneira semelhante a getPlaceById().

Siga estas etapas para buscar um lugar:

  1. Chame fetchPlace(), transmitindo um objeto FetchPlaceRequest que especifique um ID de lugar e uma lista de campos que especifiquem os dados de lugar a serem retornados.

    // 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. Chame addOnSuccessListener() para processar o FetchPlaceResponse. Um único resultado Place é retornado.

    // 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());
        }
    });
    

Buscar uma foto de um lugar

Use fetchPhoto() para receber uma foto de um lugar. fetchPhoto() retorna fotos de um lugar. O padrão para solicitar uma foto foi simplificado. Agora é possível solicitar PhotoMetadata diretamente do objeto Place. Uma solicitação separada não é mais necessária. As fotos podem ter largura ou altura máxima de 1.600 px. A função fetchPhoto() funciona de maneira semelhante à getPhoto().

Siga estas etapas para buscar fotos de lugares:

  1. Configure uma chamada para fetchPlace(). Inclua o campo PHOTO_METADATAS na solicitação:

    List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
    
  2. Receba um objeto Place (este exemplo usa fetchPlace(), mas você também pode usar findCurrentPlace()):

    FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
    
  3. Adicione um OnSuccessListener para receber os metadados da foto do Place resultante no FetchPlaceResponse e use os metadados da foto resultante para receber um bitmap e um texto de atribuição:

    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());
            }
        });
    });
    

Encontrar um lugar a partir do local do usuário

Use findCurrentPlace() para encontrar a localização atual do dispositivo do usuário. findCurrentPlace() retorna uma lista de PlaceLikelihoods indicando os lugares em que o dispositivo do usuário provavelmente está localizado. findCurrentPlace() funciona de maneira semelhante a getCurrentPlace().

Siga estas etapas para conferir a localização atual do dispositivo do usuário:

  1. Verifique se o app solicita as permissões ACCESS_FINE_LOCATION e ACCESS_WIFI_STATE. O usuário precisa conceder permissão para acessar a localização atual do dispositivo. Consulte Solicitar permissões de apps para mais detalhes.

  2. Crie um FindCurrentPlaceRequest, incluindo uma lista de tipos de dados de lugar a serem retornados.

      // 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. Chame findCurrentPlace e processe a resposta, verificando primeiro se o usuário concedeu permissão para usar a localização do dispositivo.

      // 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();
      }
    

Encontrar previsões de preenchimento automático

Use findAutocompletePredictions() para retornar previsões de lugares em resposta a consultas de pesquisa do usuário. findAutocompletePredictions() funciona de maneira semelhante a getAutocompletePredictions().

O exemplo a seguir mostra como chamar 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());
   }
});

Tokens de sessão

Os tokens de sessão agrupam as fases de consulta e seleção de uma pesquisa do usuário em uma sessão discreta para fins de faturamento. Recomendamos o uso de tokens de sessão em todas as sessões de preenchimento automático. A sessão começa quando a pessoa começa a digitar uma consulta e termina quando seleciona um lugar. Cada sessão pode ter várias consultas, seguidas por uma seleção de lugar. Após a conclusão de uma sessão, o token perde a validade. Seu app precisa gerar um novo token para cada sessão.

Máscaras de campo

Em métodos que retornam detalhes do lugar, é necessário especificar quais tipos de dados do lugar foram retornados em cada solicitação. Isso ajuda a garantir que você solicite e pague apenas os dados que vai usar.

Para especificar quais tipos de dados retornar, transmita uma matriz de Place.Fields no FetchPlaceRequest, conforme mostrado no exemplo abaixo:

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

Para conferir uma lista de campos que podem ser usados em uma máscara de campo, consulte Campos de dados de lugar (novo) .

Leia mais sobre as SKUs de dados do Places.

Atualizações do Place Picker e do Autocomplete

Esta seção explica as mudanças nos widgets do Places (Place Picker e Autocomplete).

Preenchimento automático programático

As seguintes mudanças foram feitas no preenchimento automático:

  • PlaceAutocomplete foi renomeado como Autocomplete.
    • PlaceAutocomplete.getPlace foi renomeado como Autocomplete.getPlaceFromIntent.
    • PlaceAutocomplete.getStatus foi renomeado como Autocomplete.getStatusFromIntent.
  • PlaceAutocomplete.RESULT_ERROR foi renomeado como AutocompleteActivity.RESULT_ERROR (o tratamento de erros do fragmento de preenchimento automático NÃO mudou)

Seletor de local

O Place Picker foi descontinuado em 29 de janeiro de 2019. Ele foi desativado em 29 de julho de 2019 e não está mais disponível. O uso contínuo vai resultar em uma mensagem de erro. O novo SDK não oferece suporte ao Place Picker.

Widgets de preenchimento automático

Os widgets de preenchimento automático foram atualizados:

  • O prefixo Place foi removido de todas as classes.
  • Adição de suporte a tokens de sessão. O widget gerencia tokens para você automaticamente em segundo plano.
  • Foi adicionado suporte a máscaras de campo, que permitem escolher quais tipos de dados de lugar fornecer depois que o usuário faz uma seleção.

As seções a seguir mostram como adicionar um widget de preenchimento automático ao seu projeto.

Incorporar um AutocompleteFragment

Para adicionar um fragmento de preenchimento automático, siga estas etapas:

  1. Adicione um fragmento ao layout XML da atividade, conforme mostrado no exemplo a seguir.

    <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. Para adicionar o widget de preenchimento automático à atividade, siga estas etapas:

    • Inicialize Places, transmitindo o contexto do aplicativo e a chave de API.
    • Inicialize o AutocompleteSupportFragment:
    • Chame setPlaceFields() para indicar os tipos de dados de lugar que você quer receber.
    • Adicione um PlaceSelectionListener para fazer algo com o resultado e lidar com os erros que possam ocorrer.

    O exemplo abaixo mostra como adicionar um widget de preenchimento automático a uma atividade:

    /**
     * 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);
        }
    });
    

Usar uma intent para iniciar a atividade de preenchimento automático

  1. Inicializar Places, transmitindo o contexto do app e a chave de API
  2. Use Autocomplete.IntentBuilder para criar uma intent, transmitindo o modo PlaceAutocomplete desejado (tela cheia ou sobreposição). A intent precisa chamar startActivityForResult, transmitindo um código de solicitação que identifica a intent.
  3. Substitua o callback onActivityResult para receber o lugar selecionado.

O exemplo a seguir mostra como usar uma intent para iniciar o preenchimento automático e processar o resultado:

    /**
     * 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.
            }
        }
    }

O Place Picker não está mais disponível

O Place Picker foi descontinuado em 29 de janeiro de 2019. Ele foi desativado em 29 de julho de 2019 e não está mais disponível. O uso contínuo vai resultar em uma mensagem de erro. O novo SDK não oferece suporte ao Place Picker.