Using Google place API, you can find places near a location and get place details such as name, address, phone, etc. Google place API contains PlacePicker and Autocomplete UI widgets and GeoDataClient and PlaceDetectionClient APIs. In this post, you can learn how to use PlacePicker UI widget with place notes example Android app. PlacePicker UI widget displays places near a location on Google map and allows user to select a place.
To know how to get list of places near current location of device, please see nearby places PlaceDetectionClient API example.
To use Google places API, you need to create a project in Google developer console, enable API, create Key, add key to android manifest xml file and add libraries to build.gradle files, for detailed instructions on how to setup your for project to use Places API, please see nearby places Android example.
The example app first lets user pick a location by using place picker. Then it allows user to enter notes for the selected place and save it. View button in menu lets user to view list of save places and notes.
To use place picker, instantiate PlacePicker.IntentBuilder object optionally passing LatLngBounds to set location for the map to display. If LatLngBounds is not passed to the builder, place picker will show current device locations. Then start place picker activity by calling startActivityForResult passing the intent create using PlacePicker.IntentBuilder.
LatLngBounds latLngBounds = new LatLngBounds(new LatLng(47.64299816, -122.14351988),
new LatLng(47.64299816, -122.14351988));
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
builder.setLatLngBounds(latLngBounds);
try {
startActivityForResult(builder.build(this), PLACE_PICKER_REQ_CODE);
} catch (Exception e) {
Log.e(TAG, e.getStackTrace().toString());
}
After the above step, place picker will show Google map as shown below.
You can drag up the nearby places list to view complete places list as shown below.
You can select a place either on the map or from the list. After selecting a place, place picker will show a confirmation screen as shown below
After you click select on the above screen, the selected place can be captured in onActivityResult method in the activity.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == LOC_REQ_CODE) {
if (resultCode == RESULT_OK) {
showPlacePicker();
}
}else if(requestCode == PLACE_PICKER_REQ_CODE){
if (resultCode == RESULT_OK) {
place = PlacePicker.getPlace(this, data);
name.setText(place.getName());
}
}
}
After place picker flow is complete, screen will display activity layout. Our example layout displays the selected place name and allows user to enter notes. On submitting save, the place details and notes will be saved in Firestore database.
There is a menu action that will start an activity to view place notes in recycler view.
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import java.text.SimpleDateFormat;
public class PlacePickerActivity extends AppCompatActivity {
public static final String TAG = "PlacePickerActivity";
private static final int LOC_REQ_CODE = 1;
private static final int PLACE_PICKER_REQ_CODE = 2;
private FirebaseFirestore firestoreDB;
private Place place;
private TextView name;
private EditText notes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.place_notes);
Toolbar tb = findViewById(R.id.toolbar);
setSupportActionBar(tb);
tb.setSubtitle("Place Picker");
name = findViewById(R.id.place_name);
notes = findViewById(R.id.notes);
findViewById(R.id.save_notes_b).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
savePlaceNotes();
}
});
firestoreDB = FirebaseFirestore.getInstance();
getCurrentPlaceItems();
}
private void getCurrentPlaceItems() {
if (isLocationAccessPermitted()) {
showPlacePicker();
} else {
requestLocationAccessPermission();
}
}
@SuppressLint("MissingPermission")
private void showPlacePicker() {
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
try {
startActivityForResult(builder.build(this), PLACE_PICKER_REQ_CODE);
} catch (Exception e) {
Log.e(TAG, e.getStackTrace().toString());
}
}
private boolean isLocationAccessPermitted() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return false;
} else {
return true;
}
}
private void requestLocationAccessPermission() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LOC_REQ_CODE);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == LOC_REQ_CODE) {
if (resultCode == RESULT_OK) {
showPlacePicker();
}
}else if(requestCode == PLACE_PICKER_REQ_CODE){
if (resultCode == RESULT_OK) {
place = PlacePicker.getPlace(this, data);
name.setText(place.getName());
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.place_notes_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_place_notes_m:
showPlacePicker();
return true;
case R.id.view_place_notes_m:
viewPlaceNotes();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void savePlaceNotes(){
PlaceNotes placeNotes = new PlaceNotes();
placeNotes.setPlaceName(place.getName().toString());
placeNotes.setAddress(place.getAddress().toString());
placeNotes.setNotes(notes.getText().toString());
placeNotes.setLatitude(place.getLatLng().latitude);
placeNotes.setLongitude(place.getLatLng().longitude);
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
String date = formatter.format(System.currentTimeMillis());
placeNotes.setDate(date);
addPlaceNotesToCollection(placeNotes);
}
private void addPlaceNotesToCollection(PlaceNotes placeNotes){
firestoreDB.collection("placeNotes")
.add(placeNotes)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
@Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "document added - id "
+ documentReference.getId());
Toast.makeText(PlacePickerActivity.this,
"Place notes added to db",
Toast.LENGTH_SHORT).show();
viewPlaceNotes();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "Error adding place notes to db", e);
Toast.makeText(PlacePickerActivity.this,
"Error adding place notes to db",
Toast.LENGTH_SHORT).show();
}
});
}
public void viewPlaceNotes(){
Intent i = new Intent();
i.setClass(this, ViewPlaceNotesActivity.class);
startActivity(i);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".PlacePickerActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
<TextView
android:id="@+id/place_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|left"
android:hint="Enter notes"
android:inputType="text|textMultiLine"
android:lines="8"
android:scrollHorizontally="false"
android:scrollbars="vertical"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/place_name" />
<Button
android:id="@+id/save_notes_b"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save Place Notes"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/notes" />
</android.support.constraint.ConstraintLayout>
</LinearLayout>
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QuerySnapshot;
import java.util.ArrayList;
import java.util.List;
public class ViewPlaceNotesActivity extends AppCompatActivity {
public static final String TAG = "ViewPlaceNotesActivity";
private FirebaseFirestore firestoreDB;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_place_notes);
Toolbar tb = findViewById(R.id.toolbar);
setSupportActionBar(tb);
tb.setSubtitle("View Place Notes");
firestoreDB = FirebaseFirestore.getInstance();
recyclerView = findViewById(R.id.place_notes_lst);
LinearLayoutManager recyclerLayoutManager =
new LinearLayoutManager(this);
recyclerView.setLayoutManager(recyclerLayoutManager);
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(recyclerView.getContext(),
recyclerLayoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
getPlaceNotesFromDb();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.place_notes_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_place_notes_m:
showPlacePicker();
return true;
case R.id.view_place_notes_m:
getPlaceNotesFromDb();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void getPlaceNotesFromDb() {
firestoreDB.collection("placeNotes")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<PlaceNotes> placeNotesList = new ArrayList<PlaceNotes>();
Log.d(TAG, "results size "+task.getResult().size());
for(DocumentSnapshot doc : task.getResult()){
placeNotesList.add(doc.toObject(PlaceNotes.class));
}
PlaceNotesRecyclerViewAdapter recyclerViewAdapter = new
PlaceNotesRecyclerViewAdapter(placeNotesList,
ViewPlaceNotesActivity.this);
recyclerView.setAdapter(recyclerViewAdapter);
} else {
Log.d(TAG, "Error getting notes ", task.getException());
}
}
});
}
public void showPlacePicker(){
Intent i = new Intent();
i.setClass(this, PlacePickerActivity.class);
startActivity(i);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:orientation="vertical"
tools:context=".ViewPlaceNotesActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/place_notes_lst"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"/>
</LinearLayout>
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
public class PlaceNotesRecyclerViewAdapter extends
RecyclerView.Adapter<PlaceNotesRecyclerViewAdapter.ViewHolder> {
private List<PlaceNotes> placeNotesList;
private Context context;
public PlaceNotesRecyclerViewAdapter(List<PlaceNotes> list, Context ctx) {
placeNotesList = list;
context = ctx;
}
@Override
public int getItemCount() {
return placeNotesList.size();
}
@Override
public PlaceNotesRecyclerViewAdapter.ViewHolder
onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.place_notes_item, parent, false);
PlaceNotesRecyclerViewAdapter.ViewHolder viewHolder =
new PlaceNotesRecyclerViewAdapter.ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(PlaceNotesRecyclerViewAdapter.ViewHolder holder, int position) {
final int itemPos = position;
final PlaceNotes place = placeNotesList.get(position);
holder.name.setText(place.getPlaceName());
holder.address.setText(place.getAddress());
holder.viewNotes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewNotes(place);
}
});
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public TextView address;
public Button viewNotes;
public ViewHolder(View view) {
super(view);
name = view.findViewById(R.id.name);
address = view.findViewById(R.id.address);
viewNotes = view.findViewById(R.id.view_notes);
}
}
private void viewNotes(PlaceNotes placeNotes){
//get notes for the selected item and display in dialog
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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"
android:layout_marginTop="4dp"
android:layout_margin="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="zoftino.com.places.ViewPlaceNotesActivity">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="parent" />
<TextView
android:id="@+id/address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name"/>
<Button
android:id="@+id/view_notes"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="View Notes"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/address"/>
</android.support.constraint.ConstraintLayout>
It is required that certain attributes need to be displayed on the screen which shows places data to comply with policies and terms of using Google places API. For more information, see Google places API attributions.