ZOFTINO.COM android and web dev tutorials

Google Places API Photos Android

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.

android google places api photos example pick 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.

android google places api photos example

Activity

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);
            }
        }
    }
}

Activity Layout

<?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.