Cómo migrar al cliente del nuevo SDK de Places

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

En esta guía, se explican los cambios entre la biblioteca de compatibilidad de Places y la nueva versión independiente del SDK de Places para Android. Si usaste la biblioteca de compatibilidad de Places en lugar de migrar a la nueva versión independiente del SDK de Places para Android, en esta guía, se muestra cómo actualizar tus proyectos a fin de usar la versión nueva del SDK de Places para Android.

La única forma de acceder a las funciones y correcciones de errores del SDK de Places para Android anterior a la versión 2.6.0 será usar el SDK de Places para Android. Google recomienda que actualices la biblioteca de compatibilidad a la nueva versión del SDK de Places para Android lo antes posible.

¿Qué cambió?

Las principales áreas de cambio son las siguientes:

  • La versión nueva del SDK de Places para Android se distribuye como una biblioteca cliente estática. Antes de enero de 2019, el SDK de Places para Android estuvo disponible a través de los Servicios de Google Play. Desde entonces, se proporcionó una biblioteca de compatibilidad de Places a fin de facilitar la transición al nuevo SDK de Places para Android.
  • Existen métodos completamente nuevos.
  • Ahora se admiten máscaras de campo para métodos que muestran detalles de un lugar. Puedes usar máscaras de campo para especificar qué tipos de datos de lugar se mostrarán.
  • Se mejoraron los códigos de estado que se usan para informar errores.
  • Autocomplete ahora admite tokens de sesión.
  • El seleccionador de sitios ya no está disponible.

Acerca de la biblioteca de compatibilidad de Places

En enero de 2019, con el lanzamiento de la versión 1.0 del SDK de Places independiente para Android, Google proporcionó una biblioteca de compatibilidad a fin de ayudar con la migración de la versión fuera de servicio del SDK de Places para Android (com.google.android.gms:play-services-places).

Esta biblioteca de compatibilidad se proporcionó de forma temporal para redireccionar y traducir llamadas a la API dirigidas a la versión de Servicios de Google Play a la nueva versión independiente hasta que los desarrolladores pudieran migrar su código para usar los nombres nuevos en el SDK independiente. Para cada versión del SDK de Places para Android que se haya lanzado de la versión 1.0 a la 2.6.0, se lanzó una versión correspondiente de la biblioteca de compatibilidad de Places a fin de proporcionar una funcionalidad equivalente.

Cómo inmovilizar y dar de baja la biblioteca de compatibilidad de Places

Todas las versiones de la biblioteca de compatibilidad del SDK de Places para Android dejarán de estar disponibles el 31 de marzo de 2022. La versión 2.6.0 es la última versión de la biblioteca de compatibilidad de Places. La única forma de acceder a las funciones y correcciones de errores del SDK de Places para Android anterior a la versión 2.6.0 será mediante el SDK de Places para Android.

Google recomienda que migres al SDK de Places para Android a fin de acceder a funciones nuevas y correcciones de errores críticos para las versiones posteriores a la versión 2.6.0. Si actualmente usas la biblioteca de compatibilidad, sigue los pasos que se indican a continuación en la sección Cómo instalar el SDK de Places para Android a fin de migrar al SDK de Places para Android.

Instale la biblioteca cliente

La versión nueva del SDK de Places para Android se distribuye como una biblioteca cliente estática.

Usa Maven para agregar el SDK de Places para Android a tu proyecto de Android Studio:

  1. Si actualmente utilizas la biblioteca de compatibilidad de Google Places:

    1. Reemplaza la siguiente línea en la sección dependencies:

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

      Con esta línea para cambiar al SDK de Places para Android:

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

  2. Si actualmente usas la versión de Servicios de Play del SDK de Places para Android:

    1. Reemplaza la siguiente línea en la sección dependencies:

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

      Con esta línea para cambiar al SDK de Places para Android:

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

  3. Sincroniza tu proyecto de Gradle.

  4. Establece minSdkVersion para el proyecto de tu aplicación en 16 o una versión posterior.

  5. Actualiza tus recursos de la tecnología de 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. Compila tu app. Si observas errores de compilación debido a tu conversión al SDK de Places para Android, consulta las secciones a continuación para obtener información sobre cómo resolverlos.

Inicializa el cliente nuevo del SDK de Places

Inicializa el cliente nuevo del SDK de Places como se muestra en el siguiente ejemplo:

// 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 estado

Cambió el código de estado para errores de límite de QPS. Los errores de límite de QPS ahora se muestran a través de PlaceStatusCodes.OVER_QUERY_LIMIT. No hay más límites de QPD.

Se agregaron los siguientes códigos de estado:

  • REQUEST_DENIED: se rechazó la solicitud. A continuación, se detallan algunas de las razones posibles:

    • No se proporcionó ninguna clave de API.
    • Se proporcionó una clave de API no válida.
    • La API de Places no se habilitó en Cloud Console.
    • Se proporcionó una clave de API con restricciones de clave incorrectas.
  • INVALID_REQUEST: la solicitud no es válida debido a un argumento faltante o no válido.

  • NOT_FOUND: no se encontró ningún resultado para la solicitud dada.

Métodos nuevos

La nueva versión del SDK de Places para Android presenta métodos nuevos, que se diseñaron para mantener la coherencia. Todos los métodos nuevos cumplen con lo siguiente:

  • Los extremos ya no usan el verbo get.
  • Los objetos de solicitud y respuesta comparten el mismo nombre que el método de cliente correspondiente.
  • Los objetos de solicitud ahora tienen compiladores; los parámetros obligatorios se pasan como parámetros de compilador de solicitudes.
  • Ya no se usan los búferes.

En esta sección, se presentan los nuevos métodos y se muestra cómo funcionan.

Cómo obtener un lugar por ID

Usa fetchPlace() para obtener detalles sobre un lugar en particular. fetchPlace() funciona de manera similar a getPlaceById().

Sigue estos pasos para obtener un lugar:

  1. Llama a fetchPlace() y pasa un objeto FetchPlaceRequest que especifique un ID de lugar y una lista de campos que especifiquen los datos del lugar que se mostrarán.

    // 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.NAME);
    
    // Construct a request object, passing the place ID and fields array.
    FetchPlaceRequest request = FetchPlaceRequest.builder(placeId, placeFields)
            .build();
    
    
  2. Llama a addOnSuccessListener() para controlar FetchPlaceResponse. Se muestra un solo resultado Place.

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

Cómo obtener una foto de un lugar

Usa fetchPhoto() para obtener una foto del lugar. fetchPhoto() muestra fotos de un lugar. Se simplificó el patrón para solicitar una foto. Ahora puedes solicitar PhotoMetadata directamente desde el objeto Place; ya no se necesita una solicitud separada. Las fotos pueden tener un ancho o una altura máximos de 1,600 px. fetchPhoto() funciona de manera similar a getPhoto().

Sigue estos pasos para obtener fotos de lugares:

  1. Configura una llamada a fetchPlace(). Asegúrate de incluir el campo PHOTO_METADATAS en la solicitud:

    List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
    
  2. Obtén un objeto Place (en este ejemplo, se usa fetchPlace(), pero también puedes usar findCurrentPlace()):

    FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
    
  3. Agrega un OnSuccessListener para obtener los metadatos de la foto del Place resultante en el FetchPlaceResponse y, luego, úsalos para obtener un mapa de bits y texto de atribución:

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

Buscar un lugar desde la ubicación del usuario

Usa findCurrentPlace() para encontrar la ubicación actual del dispositivo del usuario. findCurrentPlace() muestra una lista de PlaceLikelihood que indica los lugares en los que es más probable que se encuentre el dispositivo del usuario. findCurrentPlace() funciona de manera similar a getCurrentPlace().

Sigue estos pasos para obtener la ubicación actual del dispositivo del usuario:

  1. Asegúrate de que tu app solicite los permisos ACCESS_FINE_LOCATION y ACCESS_WIFI_STATE. El usuario debe otorgar permiso para acceder a la ubicación actual de su dispositivo. Consulta Cómo solicitar permisos de la app para obtener más detalles.

  2. Crea un FindCurrentPlaceRequest, incluida una lista de los tipos de datos del lugar que se mostrarán.

      // Use fields to define the data types to return.
      List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME);
    
      // Use the builder to create a FindCurrentPlaceRequest.
      FindCurrentPlaceRequest request =
              FindCurrentPlaceRequest.builder(placeFields).build();
    
  3. Llama a findCurrentPlace y controla la respuesta. Primero verifica que el usuario haya otorgado permiso para usar la ubicación de su 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();
      }
    

Cómo buscar predicciones de autocompletar

Usa findAutocompletePredictions() para mostrar predicciones de sitios en respuesta a consultas de búsqueda de usuarios. findAutocompletePredictions() funciona de manera similar a getAutocompletePredictions().

En el siguiente ejemplo, se muestra cómo llamar a 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")
   .setTypeFilter(TypeFilter.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 sesión

Los tokens de sesión agrupan las fases de consulta y selección de una búsqueda de usuario en una sesión discreta con fines de facturación. Recomendamos usar tokens de sesión para todas las sesiones de autocompletado. La sesión comienza cuando el usuario comienza a escribir una consulta y concluye cuando selecciona un lugar. Cada sesión puede tener varias consultas, seguidas de una selección de lugar. Una vez que la sesión concluye, el token ya no es válido; tu app debe generar un token nuevo para cada sesión.

Máscaras de campo

En los métodos que muestran detalles de sitios, debes especificar qué tipos de datos de sitios se muestran con cada solicitud. Esto ayuda a garantizar que solo solicites (y pagues) los datos que realmente usarás.

Para especificar qué tipos de datos mostrar, pasa un arreglo de Place.Field en tu FetchPlaceRequest, como se muestra en el siguiente ejemplo:

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

Puedes usar uno o más de los siguientes campos:

  • Place.Field.ADDRESS
  • Place.Field.ID
  • Place.Field.LAT_LNG
  • Place.Field.NAME
  • Place.Field.OPENING_HOURS
  • Place.Field.PHONE_NUMBER
  • Place.Field.PHOTO_METADATAS
  • Place.Field.PLUS_CODE
  • Place.Field.PRICE_LEVEL
  • Place.Field.RATING
  • Place.Field.TYPES
  • Place.Field.USER_RATINGS_TOTAL
  • Place.Field.VIEWPORT
  • Place.Field.WEBSITE_URI

Obtén más información acerca de los SKU de Places Data.

Actualizaciones del autocompletado y del seleccionador de sitios

En esta sección, se explican los cambios en los widgets de Places (seleccionador de sitios y autocompletado).

Autocompletado programático

Se realizaron los siguientes cambios en el autocompletado:

  • El nombre de PlaceAutocomplete cambió por Autocomplete.
    • El nombre de PlaceAutocomplete.getPlace cambió por Autocomplete.getPlaceFromIntent.
    • El nombre de PlaceAutocomplete.getStatus cambió por Autocomplete.getStatusFromIntent.
  • Se cambió el nombre de PlaceAutocomplete.RESULT_ERROR por AutocompleteActivity.RESULT_ERROR (el control de errores del fragmento de autocompletado NO cambió).

Seleccionador de sitios

El seleccionador de sitios dejó de estar disponible el 29 de enero de 2019. Se desactivó el 29 de julio de 2019 y ya no está disponible. El uso continuo dará como resultado un mensaje de error. El nuevo SDK no es compatible con el seleccionador de sitios.

Widgets de autocompletar

Se actualizaron los widgets de autocompletado:

  • Se quitó el prefijo Place de todas las clases.
  • Se agregó compatibilidad con tokens de sesión. El widget administra los tokens por ti automáticamente en segundo plano.
  • Se agregó compatibilidad con máscaras de campo, que te permiten elegir qué tipos de datos del lugar mostrar después de que el usuario haga una selección.

En las siguientes secciones, se muestra cómo agregar un widget de autocompletado a tu proyecto.

Incorpora un AutocompleteFragment

Para agregar un fragmento de autocompletado, sigue estos pasos:

  1. Agrega un fragmento al diseño XML de tu actividad, como se muestra en el siguiente ejemplo.

    <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 agregar el widget de autocompletado a la actividad, sigue estos pasos:

    • Inicializa Places y pasa el contexto de la aplicación y tu clave de API.
    • Inicializa el objeto AutocompleteSupportFragment.
    • Llama a setPlaceFields() para indicar los tipos de datos del lugar que deseas obtener.
    • Agrega un PlaceSelectionListener para hacer algo con el resultado y manejar cualquier error que pueda ocurrir.

    En el siguiente ejemplo, se muestra cómo agregar un widget de autocompletado a una actividad:

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

Usa un intent para iniciar la actividad de autocompletado

  1. Inicializa Places y pasa el contexto de la app y tu clave de API
  2. Usa Autocomplete.IntentBuilder para crear un intent y pasa el modo PlaceAutocomplete deseado (pantalla completa o superposición). El intent debe llamar a startActivityForResult y pasar un código de solicitud que lo identifique.
  3. Anula la devolución de llamada onActivityResult para recibir el lugar seleccionado.

En el siguiente ejemplo, se muestra cómo usar un intent para iniciar el autocompletado y, luego, manejar el 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.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.
            }
        }
    }

El seleccionador de sitios ya no está disponible.

El seleccionador de sitios dejó de estar disponible el 29 de enero de 2019. Se desactivó el 29 de julio de 2019 y ya no está disponible. El uso continuo dará como resultado un mensaje de error. El nuevo SDK no es compatible con el seleccionador de sitios.