In this post, you can learn how to use Google places API to get a list of photos associated to a selected place and display them in your app.
For that, first you need to setup your android project, please see detailed instructions for setting up android project to use Google places API.
Our example first displays Google map based on current location of device allowing user to pick a place near device’s current location. This is done using PlacePicker, for more information, please see PlacePicker android example. You can even provide place search feature instead of map in your app using Google places auto complete to let users select a place.
Once selected Place is captured in the code, you can use GeoDataClient API to get photos meta-data by calling getPlacePhotos and passing the selected place id. It returns PlacePhotoMetadataResponse object, from that you can get PlacePhotoMetadataBuffer object which contains PlacePhotoMetadata object. Each PlacePhotoMetadata represents a photo.
Since our example app doesn’t download all photos associated to a selected palace all at once, it adds all PlacePhotoMetadata objects to a list for later use. To use PlacePhotoMetadata object like this, we need to call freeze on each PlacePhotoMetadata object and once all PlacePhotoMetadata objects are added to the list, we need to call release on PlacePhotoMetadataBuffer object.
final Task<PlacePhotoMetadataResponse> photoResponse =
geoDataClient.getPlacePhotos(placeId);
photoResponse.addOnCompleteListener
(new OnCompleteListener<PlacePhotoMetadataResponse>() {
@Override
public void onComplete(@NonNull Task<PlacePhotoMetadataResponse> task) {
currentPhotoIndex = 1;
photosDataList = new ArrayList<>();
PlacePhotoMetadataResponse photos = task.getResult();
PlacePhotoMetadataBuffer photoMetadataBuffer = photos.getPhotoMetadata();
for(PlacePhotoMetadata photoMetadata : photoMetadataBuffer){
photosDataList.add(photoMetadataBuffer.get(0).freeze());
}
photoMetadataBuffer.release();
}
});
To get a photo, you need to call getPhoto method on GeoDataClient object passing PlacePhotoMetadata. It returns PlacePhotoResponse object from that you can get photo as Bitmap. In our example app, once photo bitmap is available it will be displayed in ImageView.
Task<PlacePhotoResponse> photoResponse = geoDataClient.getPhoto(photoMetadata);
photoResponse.addOnCompleteListener(new OnCompleteListener<PlacePhotoResponse>() {
@Override
public void onComplete(@NonNull Task<PlacePhotoResponse> task) {
PlacePhotoResponse photo = task.getResult();
Bitmap photoBitmap = photo.getBitmap();
placeImage.setImageBitmap(photoBitmap);
placeImage.invalidate();
}
});
In our example app, options are provided to view next or previous images. Activity tracks current index, using the index, it gets PlacePhotoMetadata object from the list, download the photo and displays it.
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.gms.location.places.GeoDataClient;
import com.google.android.gms.location.places.PlacePhotoMetadata;
import com.google.android.gms.location.places.PlacePhotoMetadataBuffer;
import com.google.android.gms.location.places.PlacePhotoMetadataResponse;
import com.google.android.gms.location.places.PlacePhotoResponse;
import com.google.android.gms.location.places.Places;
import com.google.android.gms.location.places.ui.PlacePicker;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import java.util.ArrayList;
import java.util.List;
public class PlacePhotosActivity extends AppCompatActivity {
public static final String TAG = "PlacePhotosActivity";
private static final int PLACE_PICKER_REQ_CODE = 2;
private GeoDataClient geoDataClient;
private TextView placeName;
private ImageView placeImage;
private Button nextPhoto;
private Button prevPhoto;
private List<PlacePhotoMetadata> photosDataList;
private int currentPhotoIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.place_photos_layout);
Toolbar tb = findViewById(R.id.toolbar);
setSupportActionBar(tb);
tb.setSubtitle("Place Photos");
placeName = findViewById(R.id.place_name);
placeImage = findViewById(R.id.place_image);
Button placePicker = findViewById(R.id.pick_place);
placePicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showPlacePicker();
}
});
nextPhoto = findViewById(R.id.next);
nextPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nextPhoto();
}
});
prevPhoto = findViewById(R.id.prev);
prevPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
prevPhoto();
}
});
geoDataClient = Places.getGeoDataClient(this, null);
showPlacePicker();
}
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());
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == PLACE_PICKER_REQ_CODE && resultCode == RESULT_OK){
com.google.android.gms.location.places.Place place =
PlacePicker.getPlace(this, data);
placeName.setText(place.getName());
Log.d(TAG, "selected place "+place.getName());
getPhotoMetadata(place.getId());
}
}
private void getPhotoMetadata(String placeId) {
final Task<PlacePhotoMetadataResponse> photoResponse =
geoDataClient.getPlacePhotos(placeId);
photoResponse.addOnCompleteListener
(new OnCompleteListener<PlacePhotoMetadataResponse>() {
@Override
public void onComplete(@NonNull Task<PlacePhotoMetadataResponse> task) {
currentPhotoIndex = 0;
photosDataList = new ArrayList<>();
PlacePhotoMetadataResponse photos = task.getResult();
PlacePhotoMetadataBuffer photoMetadataBuffer = photos.getPhotoMetadata();
Log.d(TAG, "number of photos "+photoMetadataBuffer.getCount());
for(PlacePhotoMetadata photoMetadata : photoMetadataBuffer){
photosDataList.add(photoMetadataBuffer.get(0).freeze());
}
photoMetadataBuffer.release();
displayPhoto();
}
});
}
private void getPhoto(PlacePhotoMetadata photoMetadata){
Task<PlacePhotoResponse> photoResponse = geoDataClient.getPhoto(photoMetadata);
photoResponse.addOnCompleteListener(new OnCompleteListener<PlacePhotoResponse>() {
@Override
public void onComplete(@NonNull Task<PlacePhotoResponse> task) {
PlacePhotoResponse photo = task.getResult();
Bitmap photoBitmap = photo.getBitmap();
Log.d(TAG, "photo "+photo.toString());
placeImage.invalidate();
placeImage.setImageBitmap(photoBitmap);
}
});
}
private void nextPhoto(){
currentPhotoIndex++;
displayPhoto();
}
private void prevPhoto(){
currentPhotoIndex--;
displayPhoto();
}
private void displayPhoto(){
Log.d(TAG, "index "+currentPhotoIndex);
Log.d(TAG, "size "+photosDataList.size());
if(photosDataList.isEmpty() || currentPhotoIndex > photosDataList.size() - 1){
return;
}
getPhoto(photosDataList.get(currentPhotoIndex));
setButtonVisibility();
}
private void setButtonVisibility(){
if(currentPhotoIndex == 0){
prevPhoto.setEnabled(false);
if(photosDataList.size() < 2){
nextPhoto.setEnabled(false);
}
}else{
prevPhoto.setEnabled(true);
nextPhoto.setEnabled(true);
if(currentPhotoIndex == photosDataList.size()-1){
nextPhoto.setEnabled(false);
}
}
}
}
<?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="zoftino.com.places.PlacePhotosActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:orientation="vertical">
<Button
android:id="@+id/pick_place"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_gravity="right"
android:text="Pick Place" />
<TextView
android:id="@+id/place_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<ImageView
android:id="@+id/place_image"
android:layout_width="match_parent"
android:layout_height="350dp" />
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/prev"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prev"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/next" />
<Button
android:id="@+id/next"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/prev"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</LinearLayout>
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.