Android Nearby Places API: Autocomplete Widget - Truiton
Skip to content

Android Nearby Places API: Autocomplete Widget

Android Nearby Places API - Featured

Using the new Android nearby places API just got simpler. As now they have introduced three new ways through which one can fetch nearby locations. Just in case you are wondering whats the Android Places API have a look at this tutorial. Android Nearby Places API are the new set of APIs which provide easy access to the places database of google. Not to mention these are paid APIs but are offered in very affordable slabs, you may even get it for free if the usage is less. Although these APIs are not completely free, but still these are the best way one can fetch nearby places on an Android device. As now the nearby locations with autocomplete feature can be fetched in total four ways:

  1. PlaceAutocompleteFragment – A Fragment with Android Places API autocomplete widget.
  2. Place AutoComplete Activity with fullscreen mode – Launch a new activity to search nearby places on Android using the autocomplete widget.
  3. Place AutoComplete Activity with overlay mode – Launch an overlay to use the Android nearby places API autocomplete widget.
  4. GeoDataApi.getAutocompletePredictions() – Directly call the API to get predictions. Refer to this tutorial.

Also when speaking of Android Places API, I would like to mention that it is not only confined to above mentioned autocomplete feature of the places API. But it also supports many more features like the current place detection API, a place picker widget and an API to add a place and photos separately. Another interesting feature about these APIs is that they also provide a report API feature to validate the fact that a user is at a specific place. But the usage of this API is left at the developer’s discretion. Hence while building the flows around report API we would have to validate the location somehow.

Before starting with the Android Nearby Places API Autocomplete widget, first we would need to include the Google Play Services library in our project’s build.gradle file:

compile 'com.google.android.gms:play-services:8.4.0'

For more detailed instructions, please read: Setting up Google Play Services.

Next to use Places API Autocomplete widget you would need to specify the API key in your Application Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="com.truiton.placeapiautocompletewidget"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_API_KEY"/>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

To generate Android Nearby Places API key, please use the following steps:

  1. Create a project in Google Developers console or use an existing one.
  2. Navigate to “APIs & Auth” section, select APIs.
  3. Search and select “Google Places API for Android”.
  4. Enable it.
  5. Then go to the credentials section.
  6. Create a key for Android application.
  7. Enter your SHA1 fingerprint with package name in the desired format. Use this SHA1 fingerprint tutorial to get your fingerprint.
  8. Paste the API key in a meta tag under the application tag of your Android Manifest as shown above.

1. Android Nearby Places API PlaceAutocompleteFragment

If you are wondering what is the easiest way to search the Google places data on an android phone? Out of all the Google Places API AutoComplete widgets, this PlaceAutocompleteFragment might be the simplest solution available. But it might not be the best solution, as it has limited support for customization. But a good thing about this fragment is that it comes with a transparent border less background. Therefore it can easily blend in to your app’s colors. When you include a PlaceAutocompleteFragment in your XML it would occupy only the space required for an EditText, like shown below:

Android Nearby Places API Autocomplete Widget
Android Nearby Places API PlaceAutocompletFragment

To show a PlaceAutocompleteFragment in your activity, all you need to do is include the below mentioned fragment in your XML:

<fragment
            android:id="@+id/place_fragment"
            android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

Here when we include a fragment in our activity, internally it will take care of all the calls to the Places API for Android. But now the challenge is to receive the results in our main activity in discussion. As you might know, to perform an fragment to activity communication, the best way is to use an interface. Thankfully here to receive results we don’t need to explicitly define an interface, as a PlaceSelectionListener interface is already defined in the package. Hence for our communication we will use this interface throughout the Android Nearby Places Autocomplete Widget tutorial. To use it make sure you implement it in your activity like this:

package com.truiton.placeapiautocompletewidget;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocompleteFragment;
import com.google.android.gms.location.places.ui.PlaceSelectionListener;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;

public class MainActivity extends AppCompatActivity implements PlaceSelectionListener {

    private static final String LOG_TAG = "PlaceSelectionListener";
    private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
            new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
    private TextView locationTextView;
    private TextView attributionsTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        locationTextView = (TextView) findViewById(R.id.txt_location);
        attributionsTextView = (TextView) findViewById(R.id.txt_attributions);

        // Method #1
        PlaceAutocompleteFragment autocompleteFragment = (PlaceAutocompleteFragment)
                getFragmentManager().findFragmentById(R.id.place_fragment);
        autocompleteFragment.setOnPlaceSelectedListener(this);
        autocompleteFragment.setHint("Search a Location");
        autocompleteFragment.setBoundsBias(BOUNDS_MOUNTAIN_VIEW);

    }

    @Override
    public void onPlaceSelected(Place place) {
        Log.i(LOG_TAG, "Place Selected: " + place.getName());
        locationTextView.setText(getString(R.string.formatted_place_data, place
                .getName(), place.getAddress(), place.getPhoneNumber(), place
                .getWebsiteUri(), place.getRating(), place.getId()));
        if (!TextUtils.isEmpty(place.getAttributions())){
            attributionsTextView.setText(Html.fromHtml(place.getAttributions().toString()));
        }
    }

    @Override
    public void onError(Status status) {
        Log.e(LOG_TAG, "onError: Status = " + status.toString());
        Toast.makeText(this, "Place selection failed: " + status.getStatusMessage(),
                Toast.LENGTH_SHORT).show();
    }
}

Please Note: Full Source code is available in a repository below.

As you can see above we need to initialize the listener for the PlaceAutocompleteFragment, and if needed, set a bounds bias. By setting bounds bias, we can bias the results to a specific area but not limit to it. Meaning places outside the bounds can still be searched. In case the bound bias is not set, the places would be searched around the device’s location.

Android Nearby Places API Autocomplete Widget

The funny thing about this Place API Autocomplete Widget is that, internally it uses an IntentBuilder to launch the overlay activity itself (discussed in a section ahead). Therefore I would advise to use this PlaceAutocompleteFragment only if you are trying to jump-start your app, as its user experience is not that good.

2. Android Places API Autocomplete Widget : Fullscreen Activity

Another way of searching nearby places on your android app is to use the Places API Autocomplete widget in full screen mode. This is a clean way fetching nearby locations as according to this approach a new screen is lunched, where the user can input there place of interest and very neatly the selected location’s object is returned when the activity is finished. Although it is not required but for this example we would again use the PlaceSelectionListener, to pass the parameters neatly. To do so, have a look at the code below:

package com.truiton.placeapiautocompletewidget;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocomplete;
import com.google.android.gms.location.places.ui.PlaceSelectionListener;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;

public class MainActivity extends AppCompatActivity implements PlaceSelectionListener {

    private static final String LOG_TAG = "PlaceSelectionListener";
    private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
            new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
    private static final int REQUEST_SELECT_PLACE = 1000;
    private TextView locationTextView;
    private TextView attributionsTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        locationTextView = (TextView) findViewById(R.id.txt_location);
        attributionsTextView = (TextView) findViewById(R.id.txt_attributions);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Method #2
                try {
                    Intent intent = new PlaceAutocomplete.IntentBuilder
                            (PlaceAutocomplete.MODE_FULLSCREEN)
                            .setBoundsBias(BOUNDS_MOUNTAIN_VIEW)
                            .build(MainActivity.this);
                    startActivityForResult(intent, REQUEST_SELECT_PLACE);
                } catch (GooglePlayServicesRepairableException |
                        GooglePlayServicesNotAvailableException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onPlaceSelected(Place place) {
        Log.i(LOG_TAG, "Place Selected: " + place.getName());
        locationTextView.setText(getString(R.string.formatted_place_data, place
                .getName(), place.getAddress(), place.getPhoneNumber(), place
                .getWebsiteUri(), place.getRating(), place.getId()));
        if (!TextUtils.isEmpty(place.getAttributions())){
            attributionsTextView.setText(Html.fromHtml(place.getAttributions().toString()));
        }
    }

    @Override
    public void onError(Status status) {
        Log.e(LOG_TAG, "onError: Status = " + status.toString());
        Toast.makeText(this, "Place selection failed: " + status.getStatusMessage(),
                Toast.LENGTH_SHORT).show();
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_SELECT_PLACE) {
            if (resultCode == RESULT_OK) {
                Place place = PlaceAutocomplete.getPlace(this, data);
                this.onPlaceSelected(place);
            } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
                Status status = PlaceAutocomplete.getStatus(this, data);
                this.onError(status);
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Please Note: Full Source code is available in a repository below.

The above code would launch an activity like this:

Android Nearby Places API Autocomplete Widget

As you can see in the above code, we are receiving the Android Nearby Places API Autocomplete Widget results in the onActivityResult() method of our MainActivity, once the place is selected. If it is required we can extract the place object there and then but instead for cleanliness we have called the interface method onPlaceSelected(Place place) and onError(Status status) respectively. Where we tend to do all the processing.

3. Android Nearby Places API Autocomplete Widget : Overlay Activity

At times while on a screen, we tend to do small tasks in an overlay as it makes the app easy to use. If you are wondering how to do a nearby place search with such user experience, you have hit the right place. Recently a new widget was introduced in the Places API for Android which allows you to directly search the nearby places in an overlay EditText with the Autocomplete feature. It would look something like this:

Android Nearby Places API Autocomplete Widget- 4

To show a working example of the Places API Autocomplete widget in overlay mode, I would start this overlay from an option menu item. To do so first lets define an action in option menu:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context="com.truiton.placeapiautocompletewidget.MainActivity">
    <item
        android:id="@+id/action_search"
        android:orderInCategory="100"
        android:title="@string/action_search"
        app:showAsAction="always"/>
</menu>

Next we can define the main activity:

package com.truiton.placeapiautocompletewidget;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocomplete;
import com.google.android.gms.location.places.ui.PlaceSelectionListener;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;

public class MainActivity extends AppCompatActivity implements PlaceSelectionListener {

    private static final String LOG_TAG = "PlaceSelectionListener";
    private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
            new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
    private static final int REQUEST_SELECT_PLACE = 1000;
    private TextView locationTextView;
    private TextView attributionsTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        locationTextView = (TextView) findViewById(R.id.txt_location);
        attributionsTextView = (TextView) findViewById(R.id.txt_attributions);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_search) {
            // Method #3
            try {
                Intent intent = new PlaceAutocomplete.IntentBuilder
                        (PlaceAutocomplete.MODE_OVERLAY)
                        .setBoundsBias(BOUNDS_MOUNTAIN_VIEW)
                        .build(MainActivity.this);
                startActivityForResult(intent, REQUEST_SELECT_PLACE);
            } catch (GooglePlayServicesRepairableException |
                    GooglePlayServicesNotAvailableException e) {
                e.printStackTrace();
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPlaceSelected(Place place) {
        Log.i(LOG_TAG, "Place Selected: " + place.getName());
        locationTextView.setText(getString(R.string.formatted_place_data, place
                .getName(), place.getAddress(), place.getPhoneNumber(), place
                .getWebsiteUri(), place.getRating(), place.getId()));
        if (!TextUtils.isEmpty(place.getAttributions())){
            attributionsTextView.setText(Html.fromHtml(place.getAttributions().toString()));
        }
    }

    @Override
    public void onError(Status status) {
        Log.e(LOG_TAG, "onError: Status = " + status.toString());
        Toast.makeText(this, "Place selection failed: " + status.getStatusMessage(),
                Toast.LENGTH_SHORT).show();
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_SELECT_PLACE) {
            if (resultCode == RESULT_OK) {
                Place place = PlaceAutocomplete.getPlace(this, data);
                this.onPlaceSelected(place);
            } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
                Status status = PlaceAutocomplete.getStatus(this, data);
                this.onError(status);
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Please Note: Full Source code is available in a repository below.

As you can see in the example above the result is returned in onActivityResult() method. If it is required we can extract the place object immediately but for cleanliness I have implemented the PlaceSelectionListener, where the data can be extracted and processed. For full code, please refer to the link below:

Source Code

With this I would like to conclude this Android Nearby Places API Autocomplete Widget tutorial hoping it would help you decide which one to choose. Although according to me out of the three above mentioned Autocomplete widgets, the cleanest approach would be to use the Autocomplete widget in full screen mode. But if the situation demands, using the widget in overlay mode could also be very useful. Although if your purpose is not solved with these approaches and you need a custom solution where you need to implement the API on a custom EditText, then please refer to this tutorial. Hope this helped you, please connect with us on Twitter, Facebook, and Google+ for more updates.

12 thoughts on “Android Nearby Places API: Autocomplete Widget”

Leave a Reply

Your email address will not be published. Required fields are marked *