Google Places API Web Service Example

Filed Under: Android

Google Places API can be used to find nearby places. In this tutorial, we’ll be developing an application that displays the nearby places of our choice along with the approximate distance and time from our current location. We’ll be using the Google Places API Web Service with Distance Matrix API in the application.

Google Places API

Google Places API Web Service allows us to query places based upon a few parameters such as the type of place, whether a place is open right now etc.

A Nearby Search request is an HTTP URL of the following form:


https://maps.googleapis.com/maps/api/place/nearbysearch/output?parameters

json is the recommended output, the other being xml

The Required parameters are:

  1. key(API key)
  2. location
  3. rankby=distance or radius : If one is used the other can’t be used.

Note: rankby=distance requires specifying either of the following parameters:

  1. name: values can be mcdonalds, kfc etc.
  2. type : values can be restaurant, cafe etc.
  3. keyword

The optional parameters can be opennow, pagetoken etc.

For more details refer this page.

Google Distance Matrix API

The Distance Matrix API is used to calculate the distance and time between two or more points.

A Distance Matrix API url is of the form:


https://maps.googleapis.com/maps/api/distancematrix/outputFormat?parameters

The required parameters are origins, destinations and the key.

origins — This contains the starting point for calculating travel distance and time. We can pass more than one set of coordinates separated by pipelines(|).

We can also pass the addresses/place id instead of coordinates and the service automatically converts them into the latitude-longitude coordinates to calculate distance and duration.

Sample code:


https://maps.googleapis.com/maps/api/distancematrix/json?origins=Washington,DC&destinations=New+York+City,NY&key=YOUR_API_KEY

Optional parameters are:

  1. mode : this expects a value among driving, bicycling, walking, transit
  2. avoid : Introduces restrictions to the route such as tolls, indoor etc

For more details visit this page.

Enabling API keys

Go to https://console.developers.google.com/ and enable the following APIs:

  1. Google Maps Distance Matrix API
  2. Google Places API Web Service
  3. Google Places API for Android

Go to credentials and create a new Key. Set the key restriction to None for now.

Let’s jump onto the business end of this tutorial. We’ll be developing an application that allows us to search nearby places based on our current location and display the places in a RecyclerView.

We’ll be searching places based on the type and name keywords that’ll be entered in the EditText and separated by a space. Example: restaurant dominos or cafe vegetarian

Google Places API Example Project Structure

google places api web service example, google distance matrix api, google places api

The Project consists of a single Activity. An adapter class for the RecyclerView. A Model class that holds the data for each RecyclerView row. Two POJO classes for converting the JSON responses to Gson from the Google places API and Distance Matrix API. APIClient and ApiInterface for using Retrofit and the endpoints.

Google Places API Example Code

Add the following dependencies inside the build.gradle file


compile 'com.google.android.gms:play-services-location:10.2.1'
    compile 'com.google.android.gms:play-services-places:10.2.1'
    compile 'com.google.code.gson:gson:2.7'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'io.nlopez.smartlocation:library:3.3.1'
    compile 'com.android.support:cardview-v7:25.3.0'
    compile 'com.android.support:recyclerview-v7:25.3.0'

compile 'io.nlopez.smartlocation:library:3.3.1' is a LocationTracking third party library that reduces the boilerplate code.

The APIClient.java code is given below:


package com.journaldev.nearbyplaces;

import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class APIClient {

    private static Retrofit retrofit = null;

    public static final String GOOGLE_PLACE_API_KEY = "ADD_YOUR_API_KEY_HERE";

    public static String base_url = "https://maps.googleapis.com/maps/api/";

    public static Retrofit getClient() {

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder().readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).addInterceptor(interceptor).build();


        retrofit = null;

        retrofit = new Retrofit.Builder()
                .baseUrl(base_url)
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();


        return retrofit;
    }

}

The ApiInterface.java code is given below


package com.journaldev.nearbyplaces;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiInterface {

    @GET("place/nearbysearch/json?")
    Call<PlacesPOJO.Root> doPlaces(@Query(value = "type", encoded = true) String type, @Query(value = "location", encoded = true) String location, @Query(value = "name", encoded = true) String name, @Query(value = "opennow", encoded = true) boolean opennow, @Query(value = "rankby", encoded = true) String rankby, @Query(value = "key", encoded = true) String key);


    @GET("distancematrix/json") // origins/destinations:  LatLng as string
    Call<ResultDistanceMatrix> getDistance(@Query("key") String key, @Query("origins") String origins, @Query("destinations") String destinations);
}

PlacesPOJO.java is the file which holds the response from Places API. Its code is given below


package com.journaldev.nearbyplaces;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class PlacesPOJO {

    public class Root implements Serializable {

        @SerializedName("results")
        public List<CustomA> customA = new ArrayList<>();
        @SerializedName("status")
        public String status;
    }

    public class CustomA implements Serializable {


        @SerializedName("geometry")
        public Geometry geometry;
        @SerializedName("vicinity")
        public String vicinity;
        @SerializedName("name")
        public String name;

    }

    public class Geometry implements Serializable{

        @SerializedName("location")
        public LocationA locationA;

    }

    public class LocationA implements Serializable {

        @SerializedName("lat")
        public String lat;
        @SerializedName("lng")
        public String lng;


    }



}

ResultDistanceMatrix.java class holds the response from Distance Matrix API. It’s code is given below:


package com.journaldev.nearbyplaces;

import com.google.gson.annotations.SerializedName;

import java.util.List;

public class ResultDistanceMatrix {
    @SerializedName("status")
    public String status;

    @SerializedName("rows")
    public List<InfoDistanceMatrix> rows;

    public class InfoDistanceMatrix {
        @SerializedName("elements")
        public List elements;

        public class DistanceElement {
            @SerializedName("status")
            public String status;
            @SerializedName("duration")
            public ValueItem duration;
            @SerializedName("distance")
            public ValueItem distance;


        }

        public class ValueItem {
            @SerializedName("value")
            public long value;
            @SerializedName("text")
            public String text;

        }
    }
}

The activity_main.xml file is given below


<?xml version="1.0" encoding="utf-8"?>
<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:background="#212121"
    tools:context="com.journaldev.nearbyplaces.MainActivity">


    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:textColor="@android:color/white"
        android:textColorHint="@android:color/white"
        android:text="restaurant mcdonalds"
        android:hint="type name"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toLeftOf="@+id/button"
        android:layout_toStartOf="@+id/button" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:text="Search" />


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/editText"
        android:scrollbars="vertical" />

</RelativeLayout>

The MainActivity.java class code is given below.


package com.journaldev.nearbyplaces;

import android.annotation.TargetApi;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.gms.maps.model.LatLng;
import java.util.ArrayList;
import java.util.List;

import io.nlopez.smartlocation.OnLocationUpdatedListener;
import io.nlopez.smartlocation.SmartLocation;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;

public class MainActivity extends AppCompatActivity {


    private ArrayList<String> permissionsToRequest;
    private ArrayList<String> permissionsRejected = new ArrayList<>();
    private ArrayList<String> permissions = new ArrayList<>();
    private final static int ALL_PERMISSIONS_RESULT = 101;
    List<StoreModel> storeModels;
    ApiInterface apiService;

    String latLngString;
    LatLng latLng;

    RecyclerView recyclerView;
    EditText editText;
    Button button;
    List<PlacesPOJO.CustomA> results;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        permissions.add(ACCESS_FINE_LOCATION);
        permissions.add(ACCESS_COARSE_LOCATION);

        permissionsToRequest = findUnAskedPermissions(permissions);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


            if (permissionsToRequest.size() > 0)
                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
            else {
                fetchLocation();
            }
        } else {
            fetchLocation();
        }


        apiService = APIClient.getClient().create(ApiInterface.class);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

        recyclerView.setNestedScrollingEnabled(false);
        recyclerView.setHasFixedSize(true);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        editText = (EditText) findViewById(R.id.editText);
        button = (Button) findViewById(R.id.button);


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String s = editText.getText().toString().trim();
                String[] split = s.split("\\s+");


                if (split.length != 2) {
                    Toast.makeText(getApplicationContext(), "Please enter text in the required format", Toast.LENGTH_SHORT).show();
                } else
                    fetchStores(split[0], split[1]);
            }
        });

    }

    private void fetchStores(String placeType, String businessName) {

        /**
         * For Locations In India McDonalds stores aren't returned accurately
         */

        //Call<PlacesPOJO.Root> call = apiService.doPlaces(placeType, latLngString,"\""+ businessName +"\"", true, "distance", APIClient.GOOGLE_PLACE_API_KEY);

        Call<PlacesPOJO.Root> call = apiService.doPlaces(placeType, latLngString, businessName, true, "distance", APIClient.GOOGLE_PLACE_API_KEY);
        call.enqueue(new Callback<PlacesPOJO.Root>() {
            @Override
            public void onResponse(Call<PlacesPOJO.Root> call, Response<PlacesPOJO.Root> response) {
                PlacesPOJO.Root root = response.body();


                if (response.isSuccessful()) {

                    if (root.status.equals("OK")) {

                        results = root.customA;
                        storeModels = new ArrayList<>();
                        for (int i = 0; i < results.size(); i++) {

                            if (i == 10)
                                break;
                            PlacesPOJO.CustomA info = results.get(i);


                            fetchDistance(info);

                        }

                    } else {
                        Toast.makeText(getApplicationContext(), "No matches found near you", Toast.LENGTH_SHORT).show();
                    }

                } else if (response.code() != 200) {
                    Toast.makeText(getApplicationContext(), "Error " + response.code() + " found.", Toast.LENGTH_SHORT).show();
                }


            }

            @Override
            public void onFailure(Call<PlacesPOJO.Root> call, Throwable t) {
                // Log error here since request failed
                call.cancel();
            }
        });


    }

    private ArrayList<String> findUnAskedPermissions(ArrayList<String> wanted) {
        ArrayList<String> result = new ArrayList<>();

        for (String perm : wanted) {
            if (!hasPermission(perm)) {
                result.add(perm);
            }
        }

        return result;
    }

    private boolean hasPermission(String permission) {
        if (canMakeSmores()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
            }
        }
        return true;
    }

    private boolean canMakeSmores() {
        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
    }


    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        switch (requestCode) {

            case ALL_PERMISSIONS_RESULT:
                for (String perms : permissionsToRequest) {
                    if (!hasPermission(perms)) {
                        permissionsRejected.add(perms);
                    }
                }

                if (permissionsRejected.size() > 0) {


                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                                requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                            }
                                        }
                                    });
                            return;
                        }
                    }

                } else {
                    fetchLocation();
                }

                break;
        }

    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(MainActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

    private void fetchLocation() {

        SmartLocation.with(this).location()
                .oneFix()
                .start(new OnLocationUpdatedListener() {
                    @Override
                    public void onLocationUpdated(Location location) {
                        latLngString = location.getLatitude() + "," + location.getLongitude();
                        latLng = new LatLng(location.getLatitude(), location.getLongitude());
                    }
                });
    }

    private void fetchDistance(final PlacesPOJO.CustomA info) {

        Call<ResultDistanceMatrix> call = apiService.getDistance(APIClient.GOOGLE_PLACE_API_KEY, latLngString, info.geometry.locationA.lat + "," + info.geometry.locationA.lng);
        call.enqueue(new Callback<ResultDistanceMatrix>() {
            @Override
            public void onResponse(Call<ResultDistanceMatrix> call, Response<ResultDistanceMatrix> response) {

                ResultDistanceMatrix resultDistance = response.body();
                if ("OK".equalsIgnoreCase(resultDistance.status)) {

                    ResultDistanceMatrix.InfoDistanceMatrix infoDistanceMatrix = resultDistance.rows.get(0);
                    ResultDistanceMatrix.InfoDistanceMatrix.DistanceElement distanceElement = infoDistanceMatrix.elements.get(0);
                    if ("OK".equalsIgnoreCase(distanceElement.status)) {
                        ResultDistanceMatrix.InfoDistanceMatrix.ValueItem itemDuration = distanceElement.duration;
                        ResultDistanceMatrix.InfoDistanceMatrix.ValueItem itemDistance = distanceElement.distance;
                        String totalDistance = String.valueOf(itemDistance.text);
                        String totalDuration = String.valueOf(itemDuration.text);

                        storeModels.add(new StoreModel(info.name, info.vicinity, totalDistance, totalDuration));


                        if (storeModels.size() == 10 || storeModels.size() == results.size()) {
                            RecyclerViewAdapter adapterStores = new RecyclerViewAdapter(results, storeModels);
                            recyclerView.setAdapter(adapterStores);
                        }

                    }

                }

            }

            @Override
            public void onFailure(Call<ResultDistanceMatrix> call, Throwable t) {
                call.cancel();
            }
        });

    }
}

In the above code, we start by asking for runtime permissions followed by fetching the current location using the SmartLocation Library.

Once we have that in place, we pass the first word from the EditText in the type and the second word in the name parameter of the fetchStores() method that eventually calls the Google Places API web service. We limit the search results to 10.

For each result, we calculate the distance and time from the store inside the method fetchDistance(). Once it’s done for all the stores, we populate the data inside the RecyclerViewAdapter.java class using a StoreModel.java data class.

StoreModel.java code is given below:


package com.journaldev.nearbyplaces;

public class StoreModel {


    public String name, address, distance, duration;

    public StoreModel(String name, String address, String distance, String duration) {

        this.name = name;
        this.address = address;
        this.distance = distance;
        this.duration = duration;
    }

}

The layout for each row of the RecyclerView is given in the xml below:
store_list_row.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="@dimen/activity_horizontal_margin"
    android:orientation="vertical">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="0dp"
        card_view:cardElevation="5dp">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="5dp">

            <TextView
                android:id="@+id/txtStoreName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="5dp"
                android:textColor="#212121" />

            <TextView
                android:id="@+id/txtStoreAddr"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="5dp"
                android:textColor="#212121" />

            <TextView
                android:id="@+id/txtStoreDist"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="5dp" />


        </LinearLayout>

    </android.support.v7.widget.CardView>

</LinearLayout>

The RecyclerViewAdapter.java code is given below.


public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {


    private List<PlacesPOJO.CustomA> stLstStores;
    private List<StoreModel> models;


    public RecyclerViewAdapter(List<PlacesPOJO.CustomA> stores, List<StoreModel> storeModels) {

        stLstStores = stores;
        models = storeModels;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.store_list_row, parent, false);

        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {

        holder.setData(stLstStores.get(holder.getAdapterPosition()), holder, models.get(holder.getAdapterPosition()));
    }


    @Override
    public int getItemCount() {
        return Math.min(5, stLstStores.size());
    }


    public class MyViewHolder extends RecyclerView.ViewHolder {


        TextView txtStoreName;
        TextView txtStoreAddr;
        TextView txtStoreDist;
        StoreModel model;


        public MyViewHolder(View itemView) {
            super(itemView);

            this.txtStoreDist = (TextView) itemView.findViewById(R.id.txtStoreDist);
            this.txtStoreName = (TextView) itemView.findViewById(R.id.txtStoreName);
            this.txtStoreAddr = (TextView) itemView.findViewById(R.id.txtStoreAddr);


        }


        public void setData(PlacesPOJO.CustomA info, MyViewHolder holder, StoreModel storeModel) {


            this.model = storeModel;

            holder.txtStoreDist.setText(model.distance + "\n" + model.duration);
            holder.txtStoreName.setText(info.name);
            holder.txtStoreAddr.setText(info.vicinity);


        }

    }
}

The output of the google places api example application in action is given below:
google places api, google distance matrix api, google nearby places example android

Note: Places API isn’t accurate for McDonalds and some food chains, especially for locations in India.

One workaround is to pass the value in the parameter name inside double quotes such as:


Call call = apiService.doPlaces(placeType, latLngString,"\""+ businessName +"\"", true, "distance", APIClient.GOOGLE_PLACE_API_KEY);

The output comes up like this for my location is given below:

android nearby places using google places api

This brings an end to this tutorial. You can download the final google places api example project from the link below.

Comments

  1. Hopes says:

    Can you please tech how its possible with new Google places SDK. I need to list nearby locations such as hotels atm with specific radius using ggogles new places SDK , Please write a blog abouts that ?

    1. Manpreet says:

      Please answer this question? I’m also need this new one

  2. Abhilash Karanth says:

    How to find nearby places in other locations?

    Your code is showing nearby places in current location. But i need to find out the nearby places in other location.

    Thanks in advance

    1. Anupam says:

      You need the latitude and longitude of that location. One way is to use google places autocomplete API to search for your desired place. You can fetch the lat/lng subsequently.

      1. Abhilash Karanth says:

        I am a beginner to android development. If possible, can you please share the code..?

        It will be more helpful.

  3. Piyush Gupta says:

    Hi good tutorial, it helps, thanks.
    Though i got an error “unable to cast error” for ResultDistanceMatrix class,

    Android Studio will not let me compile unless i do the below casting :-
    ResultDistanceMatrix.InfoDistanceMatrix infoDistanceMatrix = (ResultDistanceMatrix.InfoDistanceMatrix) resultDistance.rows.get(0);

    But After casting i get this error :-
    java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.android.mapapi.location.model.ResultDistanceMatrix$InfoDistanceMatrix

    1. emerson says:

      did you ever figure this out? i am having the same issue

    2. Emerson Christie says:

      I had this same issue and it turned out that the code above for ResultDistanceMatrix.java is different than that of the download. If you download the zip and use the code it will work.

  4. Mayada says:

    Can’t cast results.get(i) to CustomA class in this line
    PlacesPOJO.CustomA info = results.get(i);

    1. Anupam says:

      Hi Mayada,
      Looks like there is a formatting issue due to which the <> tags and everything inside them gets escaped. List<CustomA> is the data type. Try downloading the project source code and emulate it. Or check the codes once more, we’ve fixed the formatting issue.

      Thanks

  5. Jorge says:

    For those people getting the message “No matches found near you” just change

    this

    @GET(“place/nearbysearch/json?”)

    for this

    @GET(“place/nearbysearch/json”)

    –> The problem was that with the last character the url was this:

    /maps/api/place/nearbysearch/json?&type

    and this is wrong: ?&

    1. sandeep says:

      I have made the Changes still is not working

  6. sapra hit says:

    not match resutl and
    (root.status.equals(“null”)

  7. Nzeye says:

    hi man your code is very good,and help us to known a lot.so thanks and continue to post more tutorials.sorry my English is very bad i ‘m Burundian in Africa french country

    1. Pankaj says:

      Thanks for the kind words, we always thrive to provide best tutorials and complete examples. We will continue to do so.

  8. Vaibhavi says:

    Hello sir, I want to calculate the killometers between two location and display that route line by using google maps in struts2.So please give me suggestions for it.

  9. kunal says:

    I downloaded above project and inserted my api key but on clicking the button , nothing happens .
    I want to find nearest ngos for animals , so please help me regarding this as soon as possible .

  10. mossama says:

    Hi Anupam Chugh
    Can you please create an index posts and group each related topics with each other your articles is very useful but they are confusing because they aren’t in sequence like java tutorials
    thanks about your work and time in doing this

    1. Pankaj says:

      Please check the sidebar for the tutorials list.

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages