A new API was released for Android, by the name of Google Places API. This API offers easy to use methods, through which Google Places data can be accessed on Android. The main advantage of this API is, while developing apps, you may not need to parse data from a web service. As all the data is exchanged through GoogleApiClient automatically. Google places API for android gives full access to Google’s database for places. Although this API is not completely free, and is offered under quota based plans, but still its a good trade off when compared to the earlier approach. Android Places API has many new methods for accessing Google places database, but here we will be discussing Autocomplete feature of it, to get a place Id. Further we will be using this unique Place ID to get place details by ID.
Earlier when we had to detect places around our current location in Android, we had to make an API call to Google Maps APIs for Places, with our current latitude and longitude. But now one of the greatest things about new Android Places API is that, to get places around your current place, you don’t need to pass LatLongs. As the new Android places API handles all of this, and much more by itself. If you wish to know in detail about more features of Google Places API for Android, please refer to this Android Places API tutorial, as here we are focused on getting a place Id using autocomplete feature of Android Places API.
Before starting this Android Places API with autocomplete and getPlaceById example, first include Google Play services in your app, by adding this in the dependencies section:
compile 'com.google.android.gms:play-services-places:10.2.1'
Add this in application tag of your manifest :
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
Add these permissions in the manifest:
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/> <uses-permission android:name="android.permission.INTERNET"/>
For more details on using play services please refer to this setting up play services page. Next have a look at the steps to generate Places API for Android key
- Create a project in Google Developers console or use an existing one.
- Navigate to “APIs & Auth” section, select APIs.
- Search and select “Places API for Android”.
- Enable it.
- Then go to the credentials section.
- Create a key for Android application.
- Enter your SHA1 fingerprint with package name in the desired format. Use this SHA1 fingerprint tutorial to get your fingerprint.
- Paste the API key in a meta tag under the application tag of your Android Manifest as shown below.
<meta-data android:name="com.google.android.geo.API_KEY" android:value="{YOUR_API_KEY}"/>
Android Places API – Autocomplete with Suggestions
As you might be aware, that Google’s Places API for Web has a very powerful autocomplete suggestions tool. But also it was not easy to integrate in an Android app. Now since Google has introduced Places API for Android with support for Autocomplete suggestions, things have gone a little easy. Place Autocomplete API has now been completely reinvented and supports many features like location boundaries, filtering place types and much more. Whats even better is that this API can be directly integrated into an Android AutoCompleteTextView, where Places API suggestions could be shown, like below.
Have a look at the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <AutoCompleteTextView android:id="@+id/autoCompleteTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:hint="Enter Place Here"/> <TextView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/autoCompleteTextView" android:layout_marginTop="20dp" android:text="Selected Place:" android:textStyle="bold"/> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/header" android:layout_marginTop="20dp"/> <TextView android:id="@+id/address" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/name"/> <TextView android:id="@+id/place_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/address"/> <TextView android:id="@+id/phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/place_id" android:autoLink="phone"/> <TextView android:id="@+id/web" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/phone" android:autoLink="web"/> <TextView android:id="@+id/att" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:autoLink="web"/> <ImageView android:id="@+id/poweredBy" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/att" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:src="@drawable/powered_by_google_light"/> <ImageView android:id="@+id/truiton_image" android:layout_width="100dp" android:layout_height="100dp" android:layout_above="@+id/poweredBy" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_marginBottom="-20dp" android:src="@mipmap/truiton_short_no_back"/> </RelativeLayout>
Before making use of Android Places API for Autocomplete suggestions please remember to show the powered by Google attribution as per guidelines. Also please remember to show third party attributions (if any) as shown above.
Now before getting place id and details from an AutoCompleteTextView, we need to customize an ArrayAdapter which would be attached to this AutoCompleteTextView. This adapter would internally make calls to the Google Places API for suggestions by using GeoDataApi.getAutocompletePredictions()
method. Have a look at the code:
package com.truiton.placeapiautocomplete; import android.content.Context; import android.util.Log; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.Toast; 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.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.Iterator; import java.util.concurrent.TimeUnit; public class PlaceArrayAdapter extends ArrayAdapter<PlaceArrayAdapter.PlaceAutocomplete> implements Filterable { private static final String TAG = "PlaceArrayAdapter"; private GoogleApiClient mGoogleApiClient; private AutocompleteFilter mPlaceFilter; private LatLngBounds mBounds; private ArrayList<PlaceAutocomplete> mResultList; /** * Constructor * * @param context Context * @param resource Layout resource * @param bounds Used to specify the search bounds * @param filter Used to specify place types */ public PlaceArrayAdapter(Context context, int resource, LatLngBounds bounds, AutocompleteFilter filter) { super(context, resource); mBounds = bounds; mPlaceFilter = filter; } public void setGoogleApiClient(GoogleApiClient googleApiClient) { if (googleApiClient == null || !googleApiClient.isConnected()) { mGoogleApiClient = null; } else { mGoogleApiClient = googleApiClient; } } @Override public int getCount() { return mResultList.size(); } @Override public PlaceAutocomplete getItem(int position) { return mResultList.get(position); } private ArrayList<PlaceAutocomplete> getPredictions(CharSequence constraint) { if (mGoogleApiClient != null) { Log.i(TAG, "Executing autocomplete query for: " + constraint); PendingResult<AutocompletePredictionBuffer> results = Places.GeoDataApi .getAutocompletePredictions(mGoogleApiClient, constraint.toString(), mBounds, mPlaceFilter); // Wait for predictions, set the timeout. AutocompletePredictionBuffer autocompletePredictions = results .await(60, TimeUnit.SECONDS); final Status status = autocompletePredictions.getStatus(); if (!status.isSuccess()) { Toast.makeText(getContext(), "Error: " + status.toString(), Toast.LENGTH_SHORT).show(); Log.e(TAG, "Error getting place predictions: " + status .toString()); autocompletePredictions.release(); return null; } Log.i(TAG, "Query completed. Received " + autocompletePredictions.getCount() + " predictions."); Iterator<AutocompletePrediction> iterator = autocompletePredictions.iterator(); ArrayList resultList = new ArrayList<>(autocompletePredictions.getCount()); while (iterator.hasNext()) { AutocompletePrediction prediction = iterator.next(); resultList.add(new PlaceAutocomplete(prediction.getPlaceId(), prediction.getFullText(null))); } // Buffer release autocompletePredictions.release(); return resultList; } Log.e(TAG, "Google API client is not connected."); return null; } @Override public Filter getFilter() { Filter filter = new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null) { // Query the autocomplete API for the entered constraint mResultList = getPredictions(constraint); if (mResultList != null) { // 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(); } } }; return filter; } class PlaceAutocomplete { public CharSequence placeId; public CharSequence description; PlaceAutocomplete(CharSequence placeId, CharSequence description) { this.placeId = placeId; this.description = description; } @Override public String toString() { return description.toString(); } } }
The above code simply calls the GeoDataApi.getAutocompletePredictions()
method to get the Places API autocomplete suggestions, on change of every character. This API call also supports a timeout limit by using the await(60, TimeUnit.SECONDS)
method, where the first argument is used to specify the limit. One of the main things to be noted in the ArrayAdapter above is that, an inner class PlaceAutocomplete
is being used to store and return the description and IDs of the places to main activity where the Android Places API would be called once again for selected ID.
Android Places API – Get Place by ID
Another API which is offered under the Google Places API for Android suite is Place Details API. This API is also called using the GoogleApiClient, but a place ID is required to make the call. Using GeoDataApi.getPlaceById
method we can get all the details of requested place. I would like to remind once again here that, calling this API is not completely free, please refer to this usage limits page. Now lets have a look at the main activity class where both these Autocomplete and Place Details by ID APIs are used in a complementing way.
package com.truiton.placeapiautocomplete; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.TextView; import android.widget.Toast; 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.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 MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { private static final String LOG_TAG = "MainActivity"; private static final int GOOGLE_API_CLIENT_ID = 0; private AutoCompleteTextView mAutocompleteTextView; private TextView mNameTextView; private TextView mAddressTextView; private TextView mIdTextView; private TextView mPhoneTextView; private TextView mWebTextView; private TextView mAttTextView; private GoogleApiClient mGoogleApiClient; private PlaceArrayAdapter mPlaceArrayAdapter; private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds( new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090)); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this) .addApi(Places.GEO_DATA_API) .enableAutoManage(this, GOOGLE_API_CLIENT_ID, this) .addConnectionCallbacks(this) .build(); mAutocompleteTextView = (AutoCompleteTextView) findViewById(R.id .autoCompleteTextView); mAutocompleteTextView.setThreshold(3); mNameTextView = (TextView) findViewById(R.id.name); mAddressTextView = (TextView) findViewById(R.id.address); mIdTextView = (TextView) findViewById(R.id.place_id); mPhoneTextView = (TextView) findViewById(R.id.phone); mWebTextView = (TextView) findViewById(R.id.web); mAttTextView = (TextView) findViewById(R.id.att); mAutocompleteTextView.setOnItemClickListener(mAutocompleteClickListener); mPlaceArrayAdapter = new PlaceArrayAdapter(this, android.R.layout.simple_list_item_1, BOUNDS_MOUNTAIN_VIEW, null); mAutocompleteTextView.setAdapter(mPlaceArrayAdapter); } private AdapterView.OnItemClickListener mAutocompleteClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final PlaceArrayAdapter.PlaceAutocomplete item = mPlaceArrayAdapter.getItem(position); final String placeId = String.valueOf(item.placeId); Log.i(LOG_TAG, "Selected: " + item.description); PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi .getPlaceById(mGoogleApiClient, placeId); placeResult.setResultCallback(mUpdatePlaceDetailsCallback); Log.i(LOG_TAG, "Fetching details for ID: " + item.placeId); } }; private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback = new ResultCallback<PlaceBuffer>() { @Override public void onResult(PlaceBuffer places) { if (!places.getStatus().isSuccess()) { Log.e(LOG_TAG, "Place query did not complete. Error: " + places.getStatus().toString()); return; } // Selecting the first object buffer. final Place place = places.get(0); CharSequence attributions = places.getAttributions(); mNameTextView.setText(Html.fromHtml(place.getName() + "")); mAddressTextView.setText(Html.fromHtml(place.getAddress() + "")); mIdTextView.setText(Html.fromHtml(place.getId() + "")); mPhoneTextView.setText(Html.fromHtml(place.getPhoneNumber() + "")); mWebTextView.setText(place.getWebsiteUri() + ""); if (attributions != null) { mAttTextView.setText(Html.fromHtml(attributions.toString())); } } }; @Override public void onConnected(Bundle bundle) { mPlaceArrayAdapter.setGoogleApiClient(mGoogleApiClient); Log.i(LOG_TAG, "Google Places API connected."); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Log.e(LOG_TAG, "Google Places API connection failed with error code: " + connectionResult.getErrorCode()); Toast.makeText(this, "Google Places API connection failed with error code:" + connectionResult.getErrorCode(), Toast.LENGTH_LONG).show(); } @Override public void onConnectionSuspended(int i) { mPlaceArrayAdapter.setGoogleApiClient(null); Log.e(LOG_TAG, "Google Places API connection suspended."); } }
This would result in a screen like this:
Now lets understand, how to get place suggestions from the API, in an AutoCompleteTextView using the GeoDataApi.getAutocompletePredictions()
method and further displaying the detailed information using GeoDataApi.getPlaceById
method on screen. To get full understanding on how this works have a look at code sample repository below:
As you can see the code above, first an instance of GoogleApiClient is created. Further it is set in the adapter as soon as the connection is established using GoogleApiClient.ConnectionCallbacks
interface. Next the PlaceArrayAdapter
is set using LatLngBounds
. A class used for defining latitude and longitude bounds. Here in the Android Places API Autocomplete example above, you can see that I have been using the latitudes and longitudes of Mountain View to set the LatLngBounds
. Now to get place details we need to call getPlaceById method in the onItemClick
method of AdapterView.OnItemClickListener
, which is set while creating the activity. This as a whole would successfully show Google Places API autocomplete predictions in the AutocompleteTextView. But if you would like to use a widget for Autocomplete predictions, refer to this tutorial. Hope this helps. Connect with us on Facebook, Google+ and Twitter for more updates.
Born in New Delhi, India. A software engineer by profession, an android enthusiast and an evangelist. My motive here is to create a group of skilled developers, who can develop something new and good. Reason being programming is my passion, and also it feels good to make a device do something you want. In a very short span of time professionally I have worked with many tech firms. As of now too, I am employed as a senior engineer in a leading tech company. In total I may have worked on more than 20 projects professionally, and whenever I get spare time I share my thoughts here at Truiton.
thank you. what a great website this is, keep up the good work.
Hi,
thank for your good work. I just have a simple question I want to add an AutoCompleteFilter to the method, for example I want just the result of airports.
how I can accomplish this, could you give me an example???
Hi,
To apply a place filter please refer to these links:
https://developer.android.com/reference/com/google/android/gms/location/places/AutocompleteFilter.html
https://developers.google.com/places/supported_types
I am sure these will help you out, if you specify the type “airport”.
-Mohit
thanks for the references but I take a look to these before
could you give me example ???
Hi,
Thank You for the nice piece of information. How can i get the city or locality of a place from the above code.
Great tutorial
However when I type text really quickly in the AutoCompleteTextView I get the error:
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(-1, class android.widget.ListPopupWindow$DropDownListView) with Adapter(class com.mycompany.appointmentmanager.PlaceArrayAdapter)]
Any suggestions on a solutions?
Hello,
How to set prediction for city and country wise ?
I would like to get the data of India. (Country : in )
did u get the answer… i am also stuck for the same please help me.
Thank a lots!!
hey
its getting error .
e/paceArrayAdapter Google Api Client is not Connected.
please help
did u solve it ??
Hello I’m getting the error as unknown status, status code: 9002. Please help
Hi Sir,
Thanks for your Best Tutorial, and you save my Job sir. I am reading many tutorial for Google Place API with Autocomplete TextView but error occur after one day Trying your tutorial, my problem is solved……
So again thank you sir..
Thanks for great tutorial, work perfectly!
thanxxx…for this great tutorial plz always give that type of tutorial..
Hello, when i have use this code in eclipse with latest google api then my eclipse is hung at 100% and not run , have you any solution for this
Thanks, It very helpfull for me, thanks for your good work
You are doing such great work.
Thanks for this tutorial.
Keep helping people.Cheers
Hi great tutorial, i have a question : How to add the autocompleteText in action bar in this case ? and how to animate maps to location that is selected by the user in the search ?
hi,
If my location is chennai, I need those areas in the first order. kindly let me know how to do like this?? Based on my location which i enter. sorry for my English. pls let me know.
hello!
thank you for your awesome work. i tried to run your code but it says that the getDescription() method on line 92 of the ‘placeArrayAdapar.java’ file has been deprecated. i tried to use getFullText() instead but i could get around it. i am very much of a beginner in android. Any help in this section would be appreciated.
Thank you for this detailed example…it’s exactly what I’ve been looking for. I have one question though. In this block of code:
Iterator iterator = autocompletePredictions.iterator();
ArrayList resultList = new ArrayList(autocompletePredictions.getCount());
while (iterator.hasNext()) {
AutocompletePrediction prediction = iterator.next();
resultList.add(new PlaceAutocomplete(prediction.getPlaceId(),
prediction.getDescription()));
}
Specifically,
resultList.add(new PlaceAutocomplete(prediction.getPlaceId(), prediction.getDescription()));
I get the compilation error: PlaceAutoComplete has private access in com.google.android.gms.location.places.ui.PlaceAutocomplete;
I can’t instantiate PlaceAutoComplete, and I can’t find any references to my problem.
Here’s my import: import com.google.android.gms.location.places.ui.PlaceAutocomplete;
Here’s my gradle dependency: compile ‘com.google.android.gms:play-services-places:9.6.1’
Do you have any ideas on what might be the cause of my compile issue?
Thx.
Home to autocomplete api places filter country???
Please Help me…
AutocompletePrediction class does not contain any method like getDescription so “prediction.getDescription()” is giving an error . Can you please describe, what this method do?
Hi Nikhil,
Updated post, please use
prediction.getFullText(null)
, as earlier method has been deprecated.-Mohit
Log.i(LOG_TAG, “Selected: ” + item.description);
How to split item.description to Address line one, two, three, city, state, country, countrycode, postal code.
Or how to get Address line one, two, three, city, state, country, countrycode, postal code using placeId .
Thank you for the great work!
However, I always end up getting Status{statusCode=ERROR, resolution=null}.
@Mohit, can you help me with that?
did you already solved this?
Great !!
it is working for me.
I get the following error while open the Activity at Second time
java.lang.IllegalStateException: Already managing a GoogleApiClient with id 0
please help me
Hazom Tutorial Thank you very much
Thanks for the tutorial/ where should i add api key in the code?
hi was this error solved ?
Status{statusCode=ERROR, resolution=null}.