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 has estado usando 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 para usar la nueva versión 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 actualizar de la biblioteca de compatibilidad a la nueva versión del SDK de Places para Android lo antes posible.
Error: 9005 PLACES_API_RATE_LIMIT_EXCEEDED
¿Qué cambió?
Las principales áreas de cambio son las siguientes:
- La nueva versión 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 estaba disponible a través de los Servicios de Google Play. Desde entonces, se proporcionó una biblioteca de compatibilidad de Places para facilitar la transición al nuevo SDK de Places para Android.
- Hay métodos completamente nuevos.
- Ahora se admiten las máscaras de campo para los métodos que devuelven detalles de lugares. Puedes usar máscaras de campo para especificar qué tipos de datos de lugar se deben devolver.
- Se mejoraron los códigos de estado que se usan para informar errores.
- Ahora, Autocomplete admite tokens de sesión.
- Place Picker 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 para Android independiente, Google proporcionó una biblioteca de compatibilidad para ayudar con la migración de la versión descontinuada del SDK de Places para Android de los Servicios de Google Play (com.google.android.gms:play-services-places
).
Esta biblioteca de compatibilidad se proporcionó de forma temporal para redireccionar y traducir las llamadas a la API dirigidas a la versión de los Servicios de Google Play a la nueva versión independiente hasta que los desarrolladores pudieran migrar su código para usar los nuevos nombres en el SDK independiente. Para cada versión del SDK de Places para Android que se lanzó desde la versión 1.0 hasta la versión 2.6.0, se lanzó una versión correspondiente de la biblioteca de compatibilidad de Places para proporcionar una funcionalidad equivalente.
Se congeló y se dejó de usar la biblioteca de compatibilidad de Places
Todas las versiones de la biblioteca de compatibilidad del SDK de Places para Android dejaron 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á usar el SDK de Places para Android.
Google recomienda que migres al SDK de Places para Android para acceder a las nuevas funciones y correcciones de errores críticos de las versiones posteriores a la 2.6.0. Si actualmente usas la biblioteca de compatibilidad, sigue los pasos que se indican a continuación en la sección Instala el SDK de Places para Android para migrar al SDK de Places para Android.
Instala la biblioteca cliente
La nueva versión 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:
Si actualmente usas la biblioteca de compatibilidad de Places, ten en cuenta lo siguiente:
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:4.3.1")
Si actualmente usas la versión de Play Services del SDK de Places para Android, haz lo siguiente:
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:4.3.1")
Sincroniza tu proyecto de Gradle.
Establece el atributo
minSdkVersion
de tu proyecto de aplicación en 23 o un valor superior.Actualiza tus recursos de "Con 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
Compila tu app. Si ves errores de compilación debido a la conversión al SDK de Places para Android, consulta las siguientes secciones para obtener información sobre cómo resolver estos errores.
Inicializa el nuevo cliente del SDK de Places
Inicializa el nuevo cliente 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
Se cambió el código de estado para los errores de límite de QPS. Los errores de límite de QPS ahora se devuelven a través de PlaceStatusCodes.OVER_QUERY_LIMIT
. Ya no hay límites de QPD.
Se agregaron los siguientes códigos de estado:
REQUEST_DENIED
: Se rechazó la solicitud. A continuación, se detallan algunos de los motivos posibles:- No se proporcionó ninguna clave de API.
- Se proporcionó una clave de API no válida.
- No se habilitó la API de Places en la consola de Cloud.
- Se proporcionó una clave de API con restricciones 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 proporcionada.
Nuevos métodos
La nueva versión del SDK de Places para Android incluye métodos completamente nuevos, diseñados para brindar 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 del cliente correspondiente.
- Los objetos de solicitud ahora tienen compiladores; los parámetros obligatorios se pasan como parámetros del compilador de solicitudes.
- Ya no se usan los búferes.
En esta sección, se presentan los métodos nuevos y se muestra cómo funcionan.
Recupera 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 recuperar un lugar:
Llama a
fetchPlace()
y pasa un objetoFetchPlaceRequest
que especifique un ID de lugar y una lista de campos que especifiquen los datos de lugar que se devolverá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.DISPLAY_NAME); // Construct a request object, passing the place ID and fields array. FetchPlaceRequest request = FetchPlaceRequest.builder(placeId, placeFields) .build();
Llama a
addOnSuccessListener()
para controlar elFetchPlaceResponse
. Se devuelve un solo resultado dePlace
.// 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()); } });
Recupera una foto de lugar
Usa fetchPhoto()
para obtener una foto de un lugar. fetchPhoto()
devuelve fotos de un lugar. Se simplificó el patrón para solicitar una foto. Ahora puedes solicitar PhotoMetadata
directamente desde el objeto Place
. Ya no es necesaria una solicitud independiente.
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 recuperar fotos de lugares:
Configura una llamada a
fetchPlace()
. Asegúrate de incluir el campoPHOTO_METADATAS
en tu solicitud:List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
Obtén un objeto Place (en este ejemplo, se usa
fetchPlace()
, pero también puedes usarfindCurrentPlace()
):FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
Agrega un
OnSuccessListener
para obtener los metadatos de la foto delPlace
resultante en elFetchPlaceResponse
y, luego, usa los metadatos de la foto resultantes 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()); } }); });
Cómo encontrar un lugar desde la ubicación del usuario
Usa findCurrentPlace()
para encontrar la ubicación actual del dispositivo del usuario. findCurrentPlace()
devuelve una lista de PlaceLikelihood
s que indican 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:
Asegúrate de que tu app solicite los permisos
ACCESS_FINE_LOCATION
yACCESS_WIFI_STATE
. El usuario debe otorgar permiso para acceder a la ubicación actual del dispositivo. Consulta Cómo solicitar permisos de la app para obtener más detalles.Crea un objeto
FindCurrentPlaceRequest
, que incluye una lista de tipos de datos de lugares que se devolverán.// 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();
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 encontrar predicciones de autocompletar
Usa findAutocompletePredictions()
para devolver predicciones de lugares en respuesta a las búsquedas de los 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")
.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 sesión
Los tokens de sesión agrupan las etapas de consulta y selección de la búsqueda de un usuario en una sesión discreta para realizar la facturación correspondiente. 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 termina cuando selecciona un lugar. Cada sesión puede tener varias búsquedas, seguidas de una selección de lugar. Una vez que finaliza la sesión, el token deja de ser válido. Tu app debe generar un token nuevo para cada sesión.
Máscaras de campo
En los métodos que devuelven detalles de lugares, debes especificar qué tipos de datos de lugar se deben devolver con cada solicitud. Esto ayuda a garantizar que solo solicites (y pagues) los datos que realmente usarás.
Para especificar qué tipos de datos se devolverán, pasa un array de Place.Field
s en tu FetchPlaceRequest
, como se muestra en el siguiente ejemplo:
// 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 obtener una lista de los campos que puedes usar en una máscara de campo, consulta Campos de datos de lugares (nuevo) .
Obtén más información sobre los SKUs de Places Data.
Actualizaciones de Place Picker y Autocomplete
En esta sección, se explican los cambios en los widgets de Places (Place Picker y Autocomplete).
Autocompletado programático
Se realizaron los siguientes cambios en la función de autocompletar:
- El nombre de
PlaceAutocomplete
cambió porAutocomplete
.- El nombre de
PlaceAutocomplete.getPlace
cambió porAutocomplete.getPlaceFromIntent
. - El nombre de
PlaceAutocomplete.getStatus
cambió porAutocomplete.getStatusFromIntent
.
- El nombre de
- Se cambió el nombre de
PlaceAutocomplete.RESULT_ERROR
aAutocompleteActivity.RESULT_ERROR
(el control de errores del fragmento de autocompletar NO cambió).
Seleccionador de sitios
Place Picker 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 generará un mensaje de error. El nuevo SDK no admite el selector de lugares.
Widgets de Autocomplete
Se actualizaron los widgets de autocompletar:
- Se quitó el prefijo
Place
de todas las clases. - Se agregó compatibilidad con tokens de sesión. El widget administra los tokens automáticamente en segundo plano.
- Se agregó compatibilidad con máscaras de campo, que te permiten elegir qué tipos de datos de lugar devolver después de que el usuario realice una selección.
En las siguientes secciones, se muestra cómo agregar un widget de Autocomplete a tu proyecto.
Cómo incorporar un AutocompleteFragment
Para agregar un fragmento de autocompletado, sigue estos pasos:
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" />
Para agregar el widget de autocompletar 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 de lugar que deseas obtener. - Agrega un
PlaceSelectionListener
para hacer algo con el resultado y controlar 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.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); } });
- Inicializa
Usa un intent para iniciar la actividad de autocompletar
- Inicializa
Places
y pasa el contexto de la app y tu clave de API - Usa
Autocomplete.IntentBuilder
para crear una intención y pasar el modoPlaceAutocomplete
deseado (pantalla completa o superposición). El intent debe llamar astartActivityForResult
y pasar un código de solicitud que identifique tu intent. - 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, controlar 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.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 ya no está disponible
Place Picker 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 generará un mensaje de error. El nuevo SDK no admite el selector de lugares.