Google puts autocompletes Android API: restrictions don't work

I define my boundaries as follows:

private static final LatLngBounds BOUNDS_CHENNAI = new LatLngBounds( new LatLng(12.8339547, 80.0817007), new LatLng(13.2611661, 80.33632279999999)); // Chennai city bounds. 

Create the Goolge API client as follows:

 mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this,this) .addApi(Places.GEO_DATA_API) .addConnectionCallbacks(this) .build(); 

And using these boundaries in the adapter, do the following:

 PendingResult<AutocompletePredictionBuffer> results = Places.GeoDataApi .getAutocompletePredictions(mGoogleApiClient, constraint.toString(), mBounds, mPlaceFilter); 

According to the documentation, I should only return to me a place in the city of Chennai, but it will return me a place from around the world.

eg. When I type β€œSola”, it returns the β€œSola road” of Ahmedabad instead of showing consistent results within.

+7
android google-places-api
source share
3 answers

I have the same problem with limitations in android. I tried everything and can not solve the problem.

We are looking for other web pages, I found this in the documentation for javascript when trying to set boundaries in the area:

The results are biased to the side, but not limited to, the places contained within these boundaries.

It seems that the api documentation for android sites is not complete. We will have to wait.

+6
source share

I found a primitive workaround if you need to show the results of a specified country or neighboring countries:

 private ArrayList<PlaceAutocomplete> getAutocomplete(CharSequence constraint) { if (mGoogleApiClient.isConnected()) { // Submit the query to the autocomplete API and retrieve a PendingResult that will // contain the results when the query completes. PendingResult<AutocompletePredictionBuffer> results = Places.GeoDataApi .getAutocompletePredictions(mGoogleApiClient, constraint.toString(), mBounds, mPlaceFilter); AutocompletePredictionBuffer autocompletePredictions = results .await(60, TimeUnit.SECONDS); final Status status = autocompletePredictions.getStatus(); if (status.isSuccess()) { Log.i(LOG_TAG, "Query completed. Received " + autocompletePredictions.getCount() + " predictions."); // Copy the results into our own data structure, because we can't hold onto the buffer. // AutocompletePrediction objects encapsulate the API response (place ID and description). Iterator<AutocompletePrediction> iterator = autocompletePredictions.iterator(); ArrayList<PlaceAutocomplete> resultList = new ArrayList<>(autocompletePredictions.getCount()); while (iterator.hasNext()) { AutocompletePrediction prediction = iterator.next(); // Get the details of this prediction and copy it into a new PlaceAutocomplete object. String data = prediction.getDescription(); // here we manually checking whether description contains our needed country(ies) if (predictionIsInNeededCountry(data)) { resultList.add(new PlaceAutocomplete(prediction.getPlaceId(), prediction.getDescription(), prediction.getPrimaryText(mCharacterStyle), prediction.getSecondaryText(mCharacterStyle), prediction.getFullText(mCharacterStyle))); } } } else { Toast.makeText(mContext, "Error contacting API: " + status.toString(), Toast.LENGTH_SHORT).show(); Log.e(LOG_TAG, "Error getting autocomplete prediction API call: " + status.toString()); autocompletePredictions.release(); return null; } // Release the buffer now that all data has been copied. autocompletePredictions.release(); return resultList; } Log.e(LOG_TAG, "Google API client is not connected for autocomplete query."); return null; } private boolean predictionIsInNeededCountry(String data) { // here you can add countries (in different languages if you want) // also you can try get current country programmatically with use of Geocoder if (data.contains("Ukraine") || data.contains("") || data.contains("Ρ—")) { return true; } return false; } 

For borders, I use the current coordinates (current latitude and longitude for the southwest and for the northeast).

I also implemented another solution:

  • Get Predictions
  • Get information about each forecast (you can pass an array to immediately get all the points).
  • Compare the coordinates of the point with the current and sort them from the nearest to the farthest.
  • Show sorted by distance.

It seems to work more accurately, but requires additional queries and calculations.
In my case, I only need results from two countries, so I get the first simpler solution.

0
source share
 import android.content.Context; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.Spanned; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.TextView; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.location.places.AutocompletePrediction; import com.google.android.gms.location.places.Place; import com.google.android.gms.location.places.PlaceBuffer; import com.google.android.gms.location.places.Places; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; public class SearchPlacesActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener { protected GoogleApiClient mGoogleApiClient; private PlaceAutocompleteAdapter mAdapter; private AutoCompleteTextView mAutocompleteView; private TextView mPlaceDetailsText; private TextView mPlaceDetailsAttribution; Toolbar toolbar; private static final LatLngBounds BOUNDS_GREATER_SYDNEY = new LatLngBounds( // new LatLng(-34.041458, 150.790100), new LatLng(-33.682247, 151.383362)); new LatLng(0, 0), new LatLng(0, 0)); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Construct a GoogleApiClient for the {@link Places#GEO_DATA_API} using AutoManage // functionality, which automatically sets up the API client to handle Activity lifecycle // events. If your activity does not extend FragmentActivity, make sure to call connect() // and disconnect() explicitly. mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this, 0 /* clientId */, this) .addApi(Places.GEO_DATA_API) .build(); setContentView(R.layout.activity_search_places); //Set toolbar toolbar = (Toolbar) findViewById(R.id.maintoolbar); toolbar.setTitle("Change Location"); toolbar.setTitleTextColor(0xFFFFFFFF); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(false); // Retrieve the AutoCompleteTextView that will display Place suggestions. mAutocompleteView = (AutoCompleteTextView) findViewById(R.id.autocomplete_places); // Register a listener that receives callbacks when a suggestion has been selected mAutocompleteView.setOnItemClickListener(mAutocompleteClickListener); // Retrieve the TextViews that will display details and attributions of the selected place. mPlaceDetailsText = (TextView) findViewById(R.id.place_details); mPlaceDetailsAttribution = (TextView) findViewById(R.id.place_attribution); // Set up the adapter that will retrieve suggestions from the Places Geo Data API that cover // the entire world. mAdapter = new PlaceAutocompleteAdapter(this, mGoogleApiClient, BOUNDS_GREATER_SYDNEY, null); mAutocompleteView.setAdapter(mAdapter); // Set up the 'clear text' button that clears the text in the autocomplete view Button clearButton = (Button) findViewById(R.id.button_clear); clearButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mAutocompleteView.setText(""); } }); } /** * Listener that handles selections from suggestions from the AutoCompleteTextView that * displays Place suggestions. * Gets the place id of the selected item and issues a request to the Places Geo Data API * to retrieve more details about the place. * * @see com.google.android.gms.location.places.GeoDataApi#getPlaceById(GoogleApiClient, * String...) */ private AdapterView.OnItemClickListener mAutocompleteClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { /* Retrieve the place ID of the selected item from the Adapter. The adapter stores each Place suggestion in a AutocompletePrediction from which we read the place ID and title. */ final AutocompletePrediction item = mAdapter.getItem(position); final String placeId = item.getPlaceId(); final CharSequence primaryText = item.getPrimaryText(null); AppLog.i("Search", "Autocomplete item selected: " + primaryText); /* Issue a request to the Places Geo Data API to retrieve a Place object with additional details about the place. */ PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi .getPlaceById(mGoogleApiClient, placeId); placeResult.setResultCallback(mUpdatePlaceDetailsCallback); /*Toast.makeText(getApplicationContext(), "Clicked: " + primaryText, Toast.LENGTH_SHORT).show();*/ AppLog.i("Search", "Called getPlaceById to get Place details for " + placeId); } }; /** * Callback for results from a Places Geo Data API query that shows the first place result in * the details view on screen. */ private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback = new ResultCallback<PlaceBuffer>() { @Override public void onResult(PlaceBuffer places) { if (!places.getStatus().isSuccess()) { // Request did not complete successfully AppLog.e("Search", "Place query did not complete. Error: " + places.getStatus().toString()); places.release(); return; } // Get the Place object from the buffer. final Place place = places.get(0); // Format details of the place for display and show it in a TextView. /* mPlaceDetailsText.setText(formatPlaceDetails(getResources(), place.getName(), place.getId(), place.getAddress(), place.getPhoneNumber(), place.getWebsiteUri(),place.getLatLng()));*/ AppLog.e("LAtlng", ">>>>>>>>>>>>" + place.getLatLng()); LatLng latLng = place.getLatLng(); double lat = latLng.latitude; double lng = latLng.longitude; SearchFood.searchresult = place.getName().toString() + "," + place.getAddress().toString(); SearchActivity.searchresult = place.getName().toString() + "," + place.getAddress().toString(); onBackPressed(); places.release(); } }; private static Spanned formatPlaceDetails(Resources res, CharSequence name, String id, CharSequence address, CharSequence phoneNumber, Uri websiteUri, LatLng latLng) { AppLog.e("Search", res.getString(R.string.place_details, name, id, address, phoneNumber, websiteUri)); return Html.fromHtml(res.getString(R.string.place_details, name, id, address, phoneNumber, websiteUri)); } /** * Called when the Activity could not connect to Google Play services and the auto manager * could resolve the error automatically. * In this case the API is not available and notify the user. * * @param connectionResult can be inspected to determine the cause of the failure */ @Override public void onConnectionFailed(ConnectionResult connectionResult) { AppLog.e("Search", "onConnectionFailed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode()); // TODO(Developer): Check error code and notify the user of error state and resolution. /* Toast.makeText(this, "Could not connect to Google API Client: Error " + connectionResult.getErrorCode(), Toast.LENGTH_SHORT).show();*/ } @Override public void onBackPressed() { super.onBackPressed(); SearchPlacesActivity.this.finish(); overridePendingTransition(R.anim.trans_right_in, R.anim.trans_right_out); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == android.R.id.home) { onBackPressed(); } return super.onOptionsItemSelected(item); } } // ADAPTER CLASS import android.content.Context; import android.graphics.Typeface; import android.text.style.CharacterStyle; import android.text.style.StyleSpan; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; import com.eatcommunity.util.AppLog; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.Status; import com.google.android.gms.common.data.DataBufferUtils; import com.google.android.gms.location.places.AutocompleteFilter; import com.google.android.gms.location.places.AutocompletePrediction; import com.google.android.gms.location.places.AutocompletePredictionBuffer; import com.google.android.gms.location.places.Places; import com.google.android.gms.maps.model.LatLngBounds; import java.util.ArrayList; import java.util.concurrent.TimeUnit; public class PlaceAutocompleteAdapter extends ArrayAdapter<AutocompletePrediction> implements Filterable { private static final String TAG = "PlaceAutocompleteAdapter"; private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD); /** * Current results returned by this adapter. */ private ArrayList<AutocompletePrediction> mResultList; private GoogleApiClient mGoogleApiClient; private LatLngBounds mBounds; private AutocompleteFilter mPlaceFilter; public PlaceAutocompleteAdapter(Context context, GoogleApiClient googleApiClient, LatLngBounds bounds, AutocompleteFilter filter) { super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1); mGoogleApiClient = googleApiClient; mBounds = bounds; mPlaceFilter = filter; } public void setBounds(LatLngBounds bounds) { mBounds = bounds; } @Override public int getCount() { return mResultList.size(); } @Override public AutocompletePrediction getItem(int position) { return mResultList.get(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = super.getView(position, convertView, parent); // Sets the primary and secondary text for a row. // Note that getPrimaryText() and getSecondaryText() return a CharSequence that may contain // styling based on the given CharacterStyle. AutocompletePrediction item = getItem(position); TextView textView1 = (TextView) row.findViewById(android.R.id.text1); TextView textView2 = (TextView) row.findViewById(android.R.id.text2); textView1.setText(item.getPrimaryText(STYLE_BOLD)); textView2.setText(item.getSecondaryText(STYLE_BOLD)); return row; } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); // Skip the autocomplete query if no constraints are given. if (constraint != null) { // Query the autocomplete API for the (constraint) search string. mResultList = getAutocomplete(constraint); if (mResultList != null) { // The API successfully returned results. results.values = mResultList; results.count = mResultList.size(); } } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (results != null && results.count > 0) { // The API returned at least one result, update the data. notifyDataSetChanged(); } else { // The API did not return any results, invalidate the data set. //notifyDataSetInvalidated(); } } @Override public CharSequence convertResultToString(Object resultValue) { // Override this method to display a readable result in the AutocompleteTextView // when clicked. if (resultValue instanceof AutocompletePrediction) { return ((AutocompletePrediction) resultValue).getFullText(null); } else { return super.convertResultToString(resultValue); } } }; } private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) { if (mGoogleApiClient.isConnected()) { AppLog.i(TAG, "Starting autocomplete query for: " + constraint); // Submit the query to the autocomplete API and retrieve a PendingResult that will // contain the results when the query completes. PendingResult<AutocompletePredictionBuffer> results = Places.GeoDataApi .getAutocompletePredictions(mGoogleApiClient, constraint.toString(), mBounds, mPlaceFilter); AutocompletePredictionBuffer autocompletePredictions = results .await(60, TimeUnit.SECONDS); final Status status = autocompletePredictions.getStatus(); if (!status.isSuccess()) { /* Toast.makeText(getContext(), "Error contacting API: " + status.toString(), Toast.LENGTH_SHORT).show();*/ AppLog.e(TAG, "Error getting autocomplete prediction API call: " + status.toString()); autocompletePredictions.release(); return null; } return DataBufferUtils.freezeAndClose(autocompletePredictions); } AppLog.e(TAG, "Google API client is not connected for autocomplete query."); return null; } } <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/container_toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include android:id="@+id/maintoolbar" layout="@layout/application_toolbar" /> </LinearLayout> <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/container_toolbar"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Search Places" android:textAppearance="? android:attr/textAppearanceMedium" /> <AutoCompleteTextView android:id="@+id/autocomplete_places" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:hint="Enter your place" android:singleLine="true" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:src="@drawable/powered_by_google_light" android:visibility="gone" /> <Button android:id="@+id/button_clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Clear text" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Selelcted Place" android:textAppearance="?android:attr/textAppearanceMedium" android:visibility="gone" /> <TextView android:id="@+id/place_details" android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="all" android:text="" android:textAppearance="? android:attr/textAppearanceMedium" /> <TextView android:id="@+id/place_attribution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="all" android:paddingTop="@dimen/activity_vertical_margin" android:text="" android:textAppearance="?android:attr/textAppearanceSmall" /> </LinearLayout> </ScrollView> </RelativeLayout> Menifest File change <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOURAPIKEY" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> 
-2
source share

All Articles