Android Google Map | Fragment,Custom Marker,Draw Path,Current Location on Google map

google map integration in android, android google map in fragment, android current location on google map, android google map custom marker, android google map custom info window

Read about Android Google Map Tutorial With Example. This article includes several example to show google map in android programmatically.

First of all, we will generate maps API key to integrate google map in android. Then we will learn to create custom marker,icon,image,color.

We will also show current location on google map, then we will show google map in fragment. Also, we will draw path/route between two end points or marker.

Table of Content

1. Google Map Integration In Android Studio Tutorial Example | Maps API Key

2. Android Google Map In Fragment Example | Map Inside Fragment

3. Android Google Map Custom Info Window Button Click Example

4. Android Google Map Custom Marker Icon,Image,Color,Size,Title,Name

5. Android Current Location On Google Map Marker Example Tutorial

6. Android Google Map Draw Path/Line/Route Between Two Points/Markers

7. Android Google Map Draw Path Between Current Location And Destination





1. Google Map Integration In Android Studio Tutorial Example | Maps API Key

Today’s tutorial is about google map integration in android studio example.

We learn how to integrate google maps in your android app using maps API.

Every location based application need to have google map in it’s functionality.

It allows user to show different places and addresses efficiently and effectively.

Travel based applications, Taxi apps like uber etc. uses map feature to enhance the User experience.

For this tutorial, we need to work at two different places.

First is google developer console where we will need to create an app, enable the maps API and to get API_KEY.

After this, we need to integrate this API_KEY with our android studio project.

Map After All Steps

google map integration in android
Final Output

Work At Google Developer Console

So let us make necessary work at the google developer console.

Step 1. Creating New Project

Click on the below link to go to google developer console:

https://console.developers.google.com

Now it will open the dashboard for developer console.

It will look like below image

google map integration in android
Creating New Project

You may see different screen than above image if you are already log in the Google account.

Just find out an option to create new project or select project, mostly it is at the top bar at left corner.

Click on Create button to make new project.

After clicking the create button, you will see the screen like the below image.

Enter New Project Details

Enter your desired project name first. Then select your country.

Then after, select radio buttons as per your choice and click the Create button.

Now we have successfully created new project in developer console. It is time to enable the Maps API.

Step 2. Enabling Maps API for Android

When you click the create button, system will open a screen like the following image.

google map integration in android
Selecting Maps SDK For Android

You will see several options like Google Drive API, Gmail API etc.

Click on the Maps SDK for Android option.

System will led you to the screen like the below image.

google map integration in android
Clicking Enable Button

Just click the ENABLE button and our Maps API for our project is successfully enabled.

Step 3. Getting Final API Key

When you click on the ENABLE button, system will open a dashboard like following image.

google map integration in android
Dashboard

By default, at the left sidebar, APIs option is selected.

In the main middle scree, there are three tabs at the top bar:  Metrics, Quotas and Credentials.

Click on the Credentials tab. System will open following screen now.

Creating Credentials

There is a button named “Create credentials”, click this button.

It will create a pop up like shown in the above image. There are three options here. Select the first option called “API key”

When you click on the “API key” option, system will open one dialog box which looks like the below image.

google map integration in android
Getting Final API key

Here you have got your final API key. We will use this API key in our android studio project so copy it in notepad.

Working on Android Studio

Step 1. Build.gradle file

When you create a new project in android studio, select default activity as “Empty Activity.”

This will method of implementing map is easy to integrate map in existing application with other codes.

Do not select “Google Maps Activity“. If you want to select this activity then I have explained about this scenario later in this tutorial.

Write down the below line in your build.gradle(Module: app) file

implementation 'com.google.android.gms:play-services:12.0.1'

It will fetch all the required classes to integrate the google map.

Step 2. Changing Manifest File

Now you need to add meta data and uses-feature tags in the AndroidManifest.xml file.

Add the below source code just after the <application> tag

<meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR API key"/>

In the above code, use need to write your API key, that we just create from the google developer console.

Replace YOUR API key with your key in the above code.

Now add the following code  after the </application> tag ending.

 <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

Full source code for AndroidManifest.xml file will look like

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

</manifest>

Step 3. Main Layout

Now we just need to change activity_main.xml file

Add the below code in it

<?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_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_gravity="center"
        android:layout_height="match_parent"
        />

</android.support.constraint.ConstraintLayout>

After this, you should get the output like the below image

Second Method For Android Studio

Now create another new project in android studio.

This time select “Google Maps Activity” as a default activity as per below image.

Select Google Map Activity

When this activity is created, we just need to change one file : google_maps_api.xml

google_maps_api.xml is located under res->values directory.

In this file, we need to add our API key. See the below source code

<resources>
    
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR API key</string>
</resources>

After this, run your project and you will get your google map.





2. Android Google Map In Fragment Example | Map Inside Fragment

This post is about Android google map in fragment example.

We will implement a google map inside a fragment using supportmapfragment class.

Integration of google map inside fragment is little different than the integration in android activity.

Look of Google Map In Fragment

Prerequisite

First of all, we need to create a project in the google developer console to have a map in our fragment.

I have written a tutorial on how to integrate google map API in android studio.

First, read and understand this tutorial. After creating this example, you will have one sample app with google map.

You will also have a Google API key, which is required in this project. So go through above link first and then come back with your Google API key.

Step 1. Adding dependencies

We will add google dependencies to use some classes related to google map.

For this, add the below line in your build.gradle(Module: app) file.

 implementation 'com.google.android.gms:play-services:12.0.1'

Step 2. Manifest Changes

Add below source code in AndroidManifest.xml file right after <application> tag.

<meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR API key"/>

In the above code, you need to write your google API key in the second line.

After </application> tag, add below code

 <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

Whole source code for  AndroidManifest.xml file will look like below

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

</manifest>

Step 3. Image

In this google map, we will add one icon to the marker.

For this icon, add one image in the res->drawable directory.

You can download sample image by clicking the below link.

[sociallocker]Download Spidy Image[/sociallocker]

Step 4. Making Fragments

We will use two fragments in this example. One is for sample and another will contain google map.

Make a new fragment and give it a name “OneFragmnent

Copy and paste the following code in fragment_one.xml file

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".OneFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:textColor="#fff"
        android:gravity="center"
        android:textSize="35dp"
        android:text="One Fragment" />

</FrameLayout>

Create another fragment named “MapFragment”

Add the following coding lines in fragment_map.xml file

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".MapFragment">

    <fragment
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:id="@+id/frg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

I have just added one fragment tag. It is representing the layout of the google map.

Consider below line in <fragment> tag.

 android:name="com.google.android.gms.maps.SupportMapFragment"

Above line will convert the simple fragment into the google map fragment.

Write down the below coding lines in MapFragment.java file

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapFragment extends Fragment {

    public MapFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_map, container, false);

        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.frg);  //use SuppoprtMapFragment for using in fragment instead of activity  MapFragment = activity   SupportMapFragment = fragment
        mapFragment.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap mMap) {
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

                mMap.clear(); //clear old markers

                CameraPosition googlePlex = CameraPosition.builder()
                        .target(new LatLng(37.4219999,-122.0862462))
                        .zoom(10)
                        .bearing(0)
                        .tilt(45)
                        .build();

                mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 10000, null);

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4219999, -122.0862462))
                        .title("Spider Man")
                        .icon(bitmapDescriptorFromVector(getActivity(),R.drawable.spider)));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4629101,-122.2449094))
                        .title("Iron Man")
                        .snippet("His Talent : Plenty of money"));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.3092293,-122.1136845))
                        .title("Captain America"));
            }
        });


        return rootView;
    }

    private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) {
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
    }

}

Clarification of above code

Read the below line

SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.frg);

I have created an object of SupportMapFragment class.

If you are working in activity instead of fragment, you should use the object of the MapFragment class instead of SupportMapFragment class.

Now we will start the map using the object mapFragment.

Below source code will create the map and it’s markers.

 mapFragment.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap mMap) {
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

                mMap.clear(); //clear old markers

                CameraPosition googlePlex = CameraPosition.builder()
                        .target(new LatLng(37.4219999,-122.0862462))
                        .zoom(10)
                        .bearing(0)
                        .tilt(45)
                        .build();

                mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 10000, null);

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4219999, -122.0862462))
                        .title("Spider Man")
                        .icon(bitmapDescriptorFromVector(getActivity(),R.drawable.spider)));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4629101,-122.2449094))
                        .title("Iron Man")
                        .snippet("His Talent : Plenty of money"));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.3092293,-122.1136845))
                        .title("Captain America"));
            }
        });

Consider the following lines

 mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
 mMap.clear(); //clear old markers

First line will set the type of the map.

Second will clear all the previous markers and will clean the map.

Following code will set the camera position.

CameraPosition googlePlex = CameraPosition.builder()
                        .target(new LatLng(37.4219999,-122.0862462))
                        .zoom(10)
                        .bearing(0)
                        .tilt(45)
                        .build();

You can set the various properties like target. You need to pass latitude and longitude of the place to set the target.

Then set the zoom, bearing, tilt etc. build() method will finally set up the map.

We are setting different markers with following code

  mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4219999, -122.0862462))
                        .title("Spider Man")
                        .icon(bitmapDescriptorFromVector(getActivity(),R.drawable.spider)));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.4629101,-122.2449094))
                        .title("Iron Man")
                        .snippet("His Talent : Plenty of money"));

                mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(37.3092293,-122.1136845))
                        .title("Captain America"));

.position property defines the place of the marker using the latitude and longitude.

.title property defines the title of the marker.

.icon property will set the custom image as an icon of the marker.

.snippet property contain some additional information about the marker.

In .icon property, I have used bitmapDescriptorFromVector() method.

Source code for bitmapDescriptorFromVector() method is as below

 private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) {
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
    }

This method creates a bitmap descriptor from the given image.

Step 5. Final Codes

Last but not least, add some codes in activity_main.xml and MainActivity.java file

Write the below code in activity_main.xml

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:layout_width="0dp"
            android:layout_weight="1"
            android:id="@+id/btn1"
            android:layout_height="wrap_content"
            android:text="Fragment 1 " />
        <Button
            android:layout_width="0dp"
            android:layout_weight="1"
            android:id="@+id/btn2"
            android:layout_height="wrap_content"
            android:text="Map Fragment" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/container_frame_back"
        android:layout_marginTop="5dp"
        android:orientation="horizontal">


    </LinearLayout>

</LinearLayout>

I have taken two buttons and one linearlayout which will be our frame layout.

LinearLayout whose id is container_frame_back is our frame and we will load our both fragments in this frame.

Now code for MainActivity.java is as follwing

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button btn1, btn2;

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

        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);

        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addFragment(new OneFragment(), false, "one");
            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addFragment(new MapFragment(), false, "one");
            }
        });

    }

    public void addFragment(Fragment fragment, boolean addToBackStack, String tag) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();

        if (addToBackStack) {
            ft.addToBackStack(tag);
        }
        ft.replace(R.id.container_frame_back, fragment, tag);
        ft.commitAllowingStateLoss();
    }
}

Describing above snippet

Button click methods are as below

 btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addFragment(new OneFragment(), false, "one");
            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addFragment(new MapFragment(), false, "one");
            }
        });

When the user clicks the first button, OneFragment will be loaded in the frame.

Second button will open the Map Fragment.

addFragment() method’s code is as below

 public void addFragment(Fragment fragment, boolean addToBackStack, String tag) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();

        if (addToBackStack) {
            ft.addToBackStack(tag);
        }
        ft.replace(R.id.container_frame_back, fragment, tag);
        ft.commitAllowingStateLoss();
    }

This fragment will open the new fragment which it receives via it’s first parameter.

Second parameter will decide whether to add a fragment in back stack or not.

So it was all the word regarding android google map in fragment example.

Do not hesitate to use comment section for any query or for your valuable reviews!





3. Android Google Map Custom Info Window Button Click Example

Android Google Map Custom Info Window Button Click Example is written here.

In this tutorial, I will explain how to create custom info window layout in google map.

Info window is something like a pop up rectangle that gives some information about the marker on google map.

It is mostly useful when you want to provide quick lines about the place. You can also provide an example image of the particular marker or place.

I will teach you how to hide this info window on the button click.

We will also consider how you can implement the button click of the info window on google map in android.

Output Of Info Window

Necessary Thing

Before you start making this example, you need to have google API Key.

This API key is necessary to integrate the google map in your android app.

For generating this API Key, you need to create one app in the Google Developer Console.

You should first read this Google Map API Integration in Android Example Tutorial for this purpose.

This tutorial has mainly two parts :

  1. Work At Google Developer Console (follow this part only)
  2. Work On Android Studio

You should follow all the steps of first part only. After this you will have your Google API Key. Now follow all the below steps to create info window on google map marker.

Step 1. Making New Activity

First of all, create a new project in the android studio.

In this process, you will be asked to choose activity type right after you entered project name, directory and SDK Version.

Following image shows to select default activity.

google map integration in android, android current location on google map, android google map custom marker, android google map custom info window
Select Google Map Activity
  • Here, select Google Maps Activity as your first activity.
  • Selecting Google Maps Activity will automatically enable the google map in your android app.

Step 2. Write API Key At Right Place

After creating project with Maps Activity, you will have one xml file called “google_maps_api.xml”

Compiler automatically creates this file at res->values directory.

Source Code for this file is as the following

<resources>
    <!--
    TODO: Before you run your application, you need a Google Maps API key.

    To get one, follow this link, follow the directions and press "Create" at the end:

    https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30%3Bcom.example.parsaniahardik.googlemap_infowindow

    You can also add your credentials to an existing key, using these values:

    Package name:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    SHA-1 certificate fingerprint:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    Alternatively, follow the directions here:
    https://developers.google.com/maps/documentation/android/start#get-key

    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
    string in this file.
    -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">AIzaSy..YOUR API Key</string>
</resources>

As you can see in the above code, last lines holds the <string> </string> tags. Enter your Google API Key between these tags.

Step 3. Lion Image

As I have already said, we will add one image in our info window. So for this purpose, we have to add one image in the drawable folder.

Download a lion image by clicking the below link and copy it in drawable directory.

[sociallocker]Download a lion image[/sociallocker]

Step 4. Custom Layout For Info window

We need create one xml layout file. This file will work as a custom file for info window.

Info window will represent a layout generated by this file. So make a new xml layout file in res->layout directory.

Name of this file should be “gir_forest.xml.”

Add the below coding lines in “gir_forest.xml.”

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

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/lion"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tvgir"
        android:textSize="20sp"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:textColor="@color/colorAccent"
        android:text="Gir "/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tvd"
        android:textSize="20sp"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:textColor="@color/colorAccent"
        android:text="Gir "/>

</LinearLayout>

I have added one image view and two text views in the above file.

Image view represent the sample image of the place and two text views are providing basic information about the place or marker.

Step 5. Adapter class Info Window

Create a new Java class named “InfoWindowAdapter.java”

Copy the below code in it.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;

public class InfoWndowAdapter implements GoogleMap.InfoWindowAdapter {

    private Context context;

    public InfoWndowAdapter(Context context) {
        this.context = context.getApplicationContext();
    }

    @Override
    public View getInfoWindow(Marker marker) {
        return null;
    }

    @Override
    public View getInfoContents(Marker marker) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v =  inflater.inflate(R.layout.gir_forest, null);

        TextView tvGir = (TextView) v.findViewById(R.id.tvgir);
        TextView tvDetails = (TextView) v.findViewById(R.id.tvd);
       // TextView tvLng = (TextView) v.findViewById(R.id.tv_lng);
        tvGir.setText("Gir Forest is located in the Gujarat State of India");
        tvDetails.setText("Forest is maily known for the Asiatic Lions.");
        //tvLng.setText("Longitude:"+ latLng.longitude);
        return v;
    }


}

This class provides data to text views. Object of this class will create display view for info window.

Step 6. Changing Default Activity

You have selected Map activity as a default activity in step 1. So you should have two files

First is activity_maps.xml and second one is MapsActivity.java

Write the below Source code in activity_maps.xml

<?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=".MapsActivity" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn"
        android:text="Close All Infoviews"/>

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MapsActivity"/>

</LinearLayout>

One button and one fragment is present in the above code.

Compiler will load the google map in the fragment.

When the user will click the button, compiler will hide the info window.

Write down the following coding lines in MapsActivity.java

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleMap.OnInfoWindowClickListener {

    private GoogleMap mMap;
    private Button btn;
    private Marker marker;

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

        btn = findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                    marker.hideInfoWindow();
            }
        });

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Sydney and move the camera
        LatLng gir = new LatLng(21.169065, 70.596481);
        mMap.addMarker(new MarkerOptions().position(gir));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(gir));

        // Setting a custom info window adapter for the google map
        InfoWndowAdapter markerInfoWindowAdapter = new InfoWndowAdapter(getApplicationContext());
        googleMap.setInfoWindowAdapter(markerInfoWindowAdapter);

        // Adding and showing marker when the map is touched

                mMap.clear();
                MarkerOptions markerOptions = new MarkerOptions();
                markerOptions.position(gir);
                mMap.animateCamera(CameraUpdateFactory.newLatLng(gir));
                marker = mMap.addMarker(markerOptions);
               // marker.showInfoWindow();

                googleMap.setOnInfoWindowClickListener(this);

            }

    @Override
    public void onInfoWindowClick(Marker marker) {
        Toast.makeText(this, "Gir Forest Clicked!!!!", Toast.LENGTH_SHORT).show();
    }
}

Attending the above code

Now let us one by one see what above code is doing.

  • First read the below code
  btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                    marker.hideInfoWindow();
            }
        });
  • It is the code for button. Info window will be hidden when the user click the button.

See the below code

  // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
  • Compiler will start the process to create the google map with the help of the above code.

Following code is run when map is ready to be loaded in the fragment.

@Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Sydney and move the camera
        LatLng gir = new LatLng(21.169065, 70.596481);
        mMap.addMarker(new MarkerOptions().position(gir));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(gir));

        // Setting a custom info window adapter for the google map
        InfoWndowAdapter markerInfoWindowAdapter = new InfoWndowAdapter(getApplicationContext());
        googleMap.setInfoWindowAdapter(markerInfoWindowAdapter);

        // Adding and showing marker when the map is touched

                mMap.clear();
                MarkerOptions markerOptions = new MarkerOptions();
                markerOptions.position(gir);
                mMap.animateCamera(CameraUpdateFactory.newLatLng(gir));
                marker = mMap.addMarker(markerOptions);
               // marker.showInfoWindow();

                googleMap.setOnInfoWindowClickListener(this);

            }

Compiler will first add the marker in the map.

Then it will create an object of the InfoWindowAdapter class. After this, compiler will generate the info window with help of an object of the InfoWindowAdapter class.

Consider the below code

@Override
    public void onInfoWindowClick(Marker marker) {
        Toast.makeText(this, "Gir Forest Clicked!!!!", Toast.LENGTH_SHORT).show();
}
  • Above code will be run when the user click on the Info Window.





4. Android Google Map Custom Marker Icon, Image, Color, Size, Title, Name

This page is about Android Google Map Custom Marker Icon with Image,Color,Size,Title and Name programmatically.

Google map gives us many ways to customize the look and feel of the map. One of them is to customize the marker.

By default, google map shows the red icon marker but we can change this red icon marker with our custom image and icon.

You can also change the Title, Name, Snippet, Size etc. of the marker to make it more attractive.

We have to work with some methods and options provided by android’s in built library to accomplish our task.

In this tutorial, I will show you how to do this with one practical example in android studio.

Google Map Image

Final output for our example is as the below image

android google map custom marker
Output with Custom markers

Wait Before You Start

To integrate the google map in your android app, you need to have one Google API Key.

You need to create one app in the Google developer console to generate this API Key.

I have already written one separate article on this topic. Read it here : Android Integrate Google Map with API Key.

Just follow the “Work At Google Developer Console” part of this tutorial. After this, you will have one Google API Key which you need to write in your android studio project.

After getting Your Key, create new project in android studio.

Step 1. Default Activity

When you are creating new project in android studio, after giving project name, project directory and SDK level you need to select the activity type.

Activity type screen should look like the below picture

google map integration in android, android current location on google map, android google map custom marker
Select Google Map Activity

As you can see in the above image, there are many options like Basic Activity, Bottom Navigation Activity, Login Activity etc.

Among them select “Google Maps Activity” because it will load google map automatically to our default activity.

Thus, it will save our time and we do not need to write extra coding lines.

Step 2. Adding API Key

Now it is time to add the Google API Key, that you generated in the google developer console.

  • There is a file named “google_maps_api.xml” in the res->values directory
  • You need to write your key in this file.

Source Code for this file should look like below snippet

<resources>
    <!--
    TODO: Before you run your application, you need a Google Maps API key.

    To get one, follow this link, follow the directions and press "Create" at the end:

    https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30%3Bcom.example.parsaniahardik.google_custom_marker

    You can also add your credentials to an existing key, using these values:

    Package name:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    SHA-1 certificate fingerprint:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    Alternatively, follow the directions here:
    https://developers.google.com/maps/documentation/android/start#get-key

    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
    string in this file.
    -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">AIza... your API key here</string>
</resources>

In the above code, last line have <string > </string> tags. You should write your Google API Key just between these two tags.

Step 3. Finishing the Changes

At last we just need to update the coding structure of MapsActivity.java file.

There should be some code lines already written by the compiler.

But replace all of them with the below lines including import statements.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap mMap) {

        //work with different map types
        mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

        mMap.clear(); //clear old markers

        CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(25.1413,55.1853))
                .zoom(2)
                .bearing(0)
                .tilt(45)
                .build();

        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 10000, null);

        mMap.addMarker(new MarkerOptions()
                .position(new LatLng(27.1750, 78.0422))
                .title("Taj Mahal")
                .snippet("It is located in India")
                .rotation((float) 3.5)
                .icon(bitmapDescriptorFromVector(getApplicationContext(),R.drawable.taj)));

        mMap.addMarker(new MarkerOptions()
                .position(new LatLng(48.8584, 2.2945))
                .title("Eiffel Tower")
                .snippet("It is located in France")
                .rotation((float) 33.5)
                .icon(bitmapDescriptorFromVector(getApplicationContext(),R.drawable.effil)));

        mMap.addMarker(new MarkerOptions()
                .position(new LatLng(25.1413, 55.1853))
                .title("burj al arab")
                .snippet("It is located in Dubai")
                .rotation((float) 93.5)
                .icon(bitmapDescriptorFromVector(getApplicationContext(),R.drawable.dubai)));

        mMap.addMarker(new MarkerOptions()
                .position(new LatLng(38.9637, 35.2433))
                .title("Turkey")
                   .snippet("It is located in Turkey")
                .rotation((float) 33.5)
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_VIOLET)));


    }

    private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) {
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
    }
}

Now let us read above code step by step.

Following code snippet is first one.

SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

Above lines will simply initiate the google map.

When compiler is ready to create a google map, it will call onMapReady() method.

Following is the first few writings for onMapReady() method.

 //work with different map types
        mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

        mMap.clear(); //clear old markers

        CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(25.1413,55.1853))
                .zoom(2)
                .bearing(0)
                .tilt(45)
                .build();

First line will set the google map type. By default NORMAL MODE is selected.

You can choose other options like : HYBRID, SATELLITE, TERRAIN etc.

Then after camera options and position are specified. You need to give latitude and longitude in .target() option.

Now let us read how to customize markers.

 mMap.addMarker(new MarkerOptions()
                .position(new LatLng(27.1750, 78.0422))
                .title("Taj Mahal")
                .snippet("It is located in India")
                .rotation((float) 3.5)
                .icon(bitmapDescriptorFromVector(getApplicationContext(),R.drawable.taj)));

I have created four markers like the above code snippet.

Customizing Marker Title

.title(“Taj Mahal”) 

For customizing title of the marker, you just need to update the above line.

Customizing Marker Snippet or Text

.snippet(“It is located in India”)

Above line will help you to add some description about the marker.

Customization of Marker Icon

.icon(bitmapDescriptorFromVector(getApplicationContext(),R.drawable.taj)));

Above line helps you to have custom image as a marker icon. All you need is to save your image into drawable folder.

Here, I have used bitmapDescriptorFromVector() method to get the bitmap from the image.

Following is the source code for bitmapDescriptorFromVector() method

private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) {
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);
}

Changing Marker Color

.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_VIOLET)));

Above line will generate violet colored marker with the default shape.

Change various colors to make your marker more beautiful.





5. Android Current Location On Google Map Marker Example Tutorial

We will develop Android Current Location On Google Map with marker in this example.

Now a days, number of people using google map is increasing significantly.

So most applications are using google map to simplify the user experience.

This tutorial will guide you to show current location on google map with marker in android studio.

First we will fetch the current latitude and longitude and then we will use them to show current location marker on google map.

For getting current location, we will use google’s FusedLocationAPI.

We will update the current location marker constantly on the google map with the current movement of the user.

Last Image

After successful implementation of all the steps, you should get the following output

I am living in Rajkot, India so red marker is there.

android current location on google map

Prerequisite For This Tutorial

To go ahead with this example, you need to know some important details regarding google map and API Key.

First of all read this tutorial to get google API Key to integrate google map in android

You should create a project in google developer console and should get API Key first using above tutorial.

Do not create android studio project from above example. Just get your Google map API Key and after getting API Key, follow all the below steps.

Step 1. Opening Maps Activity

Create a new project in android studio. Give the name, directory, sdk level etc. details of new project.

After this, you will have a screen like the below image.

google map integration in android, android current location on google map
Select Google Map Activity
  • You need to select a default activity for your new project. We generally select Empty activity which have plain workspace.
  • But in this case, select Google Maps Activity as it will integrate google map automatically in the default activity.
  • You just need to update your API Key So, it will simplify our process and will save our lot of time.

Step 2. Change gradle file

Now it is time to do some changes in build.gradle(Module: app) file

By default, you will have the below line in your build.gradle(Module: app) file because of Google Maps Activity

implementation 'com.google.android.gms:play-services-maps:15.0.1'

We also require following two lines here

implementation 'com.google.android.gms:play-services-location:15.0.1'
implementation 'com.karumi:dexter:5.0.0'

First line will help us to get the current location.

Second line will fetch all the classes from dexter library to get the runtime permissions.

So final code for build.gradle(Module: app) file is as below

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.parsaniahardik.google_map_currentlocation"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //noinspection GradleCompatible
    implementation 'com.android.support:appcompat-v7:27.1.1'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation 'com.google.android.gms:play-services-maps:15.0.1'
    implementation 'com.google.android.gms:play-services-location:15.0.1'
    implementation 'com.karumi:dexter:5.0.0'

}

Step 3. Writing GOOGLE API Key

There is a file named google_maps_api.xml which is system generated.

  • This google_maps_api.xml file is available at res->values directory.
  • You need to write your Google API Key here in this file.

Code for this file is as following

<resources>
    <!--
    TODO: Before you run your application, you need a Google Maps API key.

    To get one, follow this link, follow the directions and press "Create" at the end:

    https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30%3Bcom.example.parsaniahardik.google_map_currentlocation

    You can also add your credentials to an existing key, using these values:

    Package name:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    SHA-1 certificate fingerprint:
    D7:DA:4B:AB:CB:75:20:AD:40:D6:AA:D8:31:E9:4B:E3:3A:33:36:30

    Alternatively, follow the directions here:
    https://developers.google.com/maps/documentation/android/start#get-key

    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
    string in this file.
    -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">AIzaS.... This is your Google API Key</string>
</resources>
  • Add your API key in between <string> Your API Key</string> tags.

Step 4. Updating Manifest file

By default, you will have ACCESS_COARSE_LOCATION in your AndroidManifest.xml file

  • Replace it with ACCESS_FINE_LOCATION because it is required to fetch more accurate current location.

Final code for AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.parsaniahardik.google_map_currentlocation">
    <!--
         The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
         Google Maps Android API v2, but you must specify either coarse or fine
         location permissions for the 'MyLocation' functionality. 
    -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!--
             The API key for Google Maps-based APIs is defined as a string resource.
             (See the file "res/values/google_maps_api.xml").
             Note that the API key is linked to the encryption key used to sign the APK.
             You need a different API key for each encryption key, including the release key that is used to
             sign the APK for publishing.
             You can define the keys for the debug and release targets in src/debug/ and src/release/. 
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Step 5. Final Changes

Now we need to change the code of activity_maps.xml and MapsActivity.java file

Replace your existing code of activity_maps.xml with the following one

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/latitude"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="Latitude:"
            android:textSize="18sp" />
        <TextView
            android:id="@+id/latitude_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/latitude"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/latitude"
            android:textSize="16sp" />
        <TextView
            android:id="@+id/longitude"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="Longitude:"
            android:layout_marginTop="24dp"
            android:textSize="18sp" />
        <TextView
            android:id="@+id/longitude_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/longitude"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/longitude"
            android:textSize="16sp"/>

    </RelativeLayout>

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MapsActivity" />


</LinearLayout>

  • Here I have taken four textviews which will represent the latitude and longitude values.
  • One fragment is also there, which will open a google map.
  • You will also some pre-written code in MapsActivity.java file.

Replace it with the below source code

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.single.PermissionListener;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener {

    private GoogleMap mMap;

    private static final String TAG = "MainActivity";
    private TextView mLatitudeTextView;
    private TextView mLongitudeTextView;
    private GoogleApiClient mGoogleApiClient;
    private Location mLocation;
    private LocationManager mLocationManager;
    private LocationRequest mLocationRequest;
    private com.google.android.gms.location.LocationListener listener;
    private long UPDATE_INTERVAL = 2 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 20000; /* 20 sec */

    private LocationManager locationManager;
    private LatLng latLng;
    private boolean isPermission;

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

        if (requestSinglePermission()) {
            // Obtain the SupportMapFragment and get notified when the map is ready to be used.
            //it was pre written
            SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
            mapFragment.getMapAsync(this);

            mLatitudeTextView = (TextView) findViewById((R.id.latitude_textview));
            mLongitudeTextView = (TextView) findViewById((R.id.longitude_textview));

            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();

            mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

            checkLocation(); //check whether location service is enable or not in your  phone
        }

    }


    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    //it was pre written
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        if (latLng != null) {
            mMap.addMarker(new MarkerOptions().position(latLng).title("Marker in Current Location"));
            mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }

        startLocationUpdates();

        mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

        if (mLocation == null) {
            startLocationUpdates();
        }
        if (mLocation != null) {

            // mLatitudeTextView.setText(String.valueOf(mLocation.getLatitude()));
            //mLongitudeTextView.setText(String.valueOf(mLocation.getLongitude()));
        } else {
            Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(TAG, "Connection Suspended");
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode());
    }

    @Override
    public void onLocationChanged(Location location) {
        String msg = "Updated Location: " +
                Double.toString(location.getLatitude()) + "," +
                Double.toString(location.getLongitude());
        mLatitudeTextView.setText(String.valueOf(location.getLatitude()));
        mLongitudeTextView.setText(String.valueOf(location.getLongitude()));
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        // You can now create a LatLng Object for use with maps
        latLng = new LatLng(location.getLatitude(), location.getLongitude());

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        //it was pre written
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    protected void startLocationUpdates() {
        // Create the location request
        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL)
                .setFastestInterval(FASTEST_INTERVAL);
        // Request location updates
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
                mLocationRequest, this);
        Log.d("reque", "--->>>>");
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    private boolean checkLocation() {
        if (!isLocationEnabled())
            showAlert();
        return isLocationEnabled();
    }

    private void showAlert() {
        final AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setTitle("Enable Location")
                .setMessage("Your Locations Settings is set to 'Off'.\nPlease Enable Location to " +
                        "use this app")
                .setPositiveButton("Location Settings", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {

                        Intent myIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        startActivity(myIntent);
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {

                    }
                });
        dialog.show();
    }

    private boolean isLocationEnabled() {
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
                locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

    private boolean requestSinglePermission() {

        Dexter.withActivity(this)
                .withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse response) {
                        //Single Permission is granted
                        Toast.makeText(MapsActivity.this, "Single permission is granted!", Toast.LENGTH_SHORT).show();
                        isPermission = true;
                    }

                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        // check for permanent denial of permission
                        if (response.isPermanentlyDenied()) {
                            isPermission = false;
                        }
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).check();

        return isPermission;

    }
}

Explanation of Maps Activity

Following snippet defines necessary variables.

 private GoogleMap mMap;
    private static final String TAG = "MainActivity";
    private TextView mLatitudeTextView;
    private TextView mLongitudeTextView;
    private GoogleApiClient mGoogleApiClient;
    private Location mLocation;
    private LocationManager mLocationManager;
    private LocationRequest mLocationRequest;
    private com.google.android.gms.location.LocationListener listener;
    private long UPDATE_INTERVAL = 2 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 20000; /* 20 sec */

    private LocationManager locationManager;
    private LatLng latLng;
    private boolean isPermission;
  • As you can see that GoogleMap, LocationRequest, LocationManager, Location etc. class’s objects are defined in the above code.
  • long variables UPDATE_INTERVAL and FASTEST_INTERVAL defines the specific time amount.

Now consider the following source code

  if (requestSinglePermission()) {
            // Obtain the SupportMapFragment and get notified when the map is ready to be used.
            //it was pre written
            SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
            mapFragment.getMapAsync(this);

            mLatitudeTextView = (TextView) findViewById((R.id.latitude_textview));
            mLongitudeTextView = (TextView) findViewById((R.id.longitude_textview));

            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();

            mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

            checkLocation(); //check whether location service is enable or not in your  phone
        }

First of all, compiler will check if the location permissions is granted or not. It will use requestSinglePermission() method for this purpose.

requestSinglePermission() method will return a boolean variable (true or false) . If the user grants the location permission then it will return true otherwise false.

If requestSinglePermission() return true, then compiler will start the process to fetch the current location and then it will create google map with the help of that current location.

Code snippet for requestSinglePermission() method is as below

 private boolean requestSinglePermission() {

        Dexter.withActivity(this)
                .withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse response) {
                        //Single Permission is granted
                        Toast.makeText(MapsActivity.this, "Single permission is granted!", Toast.LENGTH_SHORT).show();
                        isPermission = true;
                    }

                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        // check for permanent denial of permission
                        if (response.isPermanentlyDenied()) {
                            isPermission = false;
                        }
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).check();

        return isPermission;

 }
  • We have used Dexter library in this method. We will request for ACCESS_FINE_LOCATION in this method.

Now look at the below method

@Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        if (latLng != null) {
            mMap.addMarker(new MarkerOptions().position(latLng).title("Marker in Current Location"));
            mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
        }
    }

This method is called when google map is ready to load.

You need to provide location in terms of latitude and longitude in this method. Map will move it’s camera at this location.

Marker is also added in this method only.

onConnected(), onConnectionSuspended(), onConnectionFailed(), and onLocationChanged() methods are called by the compiler while fetching the current location.

Compiler will get the current latitude and longitude in onLocationChanged() method.

Source Code for onLocationChanged() method is as following

  @Override
    public void onLocationChanged(Location location) {
        String msg = "Updated Location: " +
                Double.toString(location.getLatitude()) + "," +
                Double.toString(location.getLongitude());
        mLatitudeTextView.setText(String.valueOf(location.getLatitude()));
        mLongitudeTextView.setText(String.valueOf(location.getLongitude()));
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        // You can now create a LatLng Object for use with maps
        latLng = new LatLng(location.getLatitude(), location.getLongitude());

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        //it was pre written
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }
  • Compiler will call this method every 20 seconds. After getting the location, compiler will again navigate the camera to the new location.
  • Hence, Google map will be updated every 20 seconds as per the current movement of the user.

The end of this tutorial. Keep navigating over DemoNuts to enhance your knowledge.





6. Android Google Map Draw Path/Line/Route Between Two Points/Markers

Learn about Android Google Map Draw Path or Polyline Between two markers tutorial example.

In this tutorial, we will draw a route between two points on google map in our android app.

If you are making an app for cab locator or something like this, you need to show path or line between two points on the google map.

This tutorial will simplify this process for you.

See the below video which is the last output of this example

Creating API on google console

First of all, we need to create a new application on google developer console.

For this, follow the “Work At Google Developer Console” part in Google Map Android Tutorial.

Once you have done above step, you will have one Google API key, which we will add in android studio project in few minutes.

After creating API key, we need to enable the Directions API.

Now, see the below image

  • At google developer console Click on the navigation menu icon as show in above image.
  • System will open one sliding menu which is looking like the below image
android google map draw path
Sliding menu
  • As per the image, click on APIs option.

It will led you to the screen like the following image

Direction API

When you click on Directions API, system will open the below screen

android google map draw path
ENABLE API
  • Click on the ENABLE button as per image.
  • Make sure that you have enabled Direction API otherwise you will not be able to draw a path between two markers.

At this stage, we have Google API key and we have enable Directions API.

Tasks on Android Studio

Now come to android studio and create a new project.

Do not select “Maps Activity”, while making new project instead select “Empty Activity.”

Now follow all the below steps.

Step 1. Writing API key

Under the res->values->strings.xml file add the below code line

 <string name="google_maps_key">YOUR_GOOGLE_API_KEY</string>
  • Replace “YOUR_GOOGLE_API_KEY” with your original API which you have already generated.

Step 2. Dependency and Manifest Works

Write down the below line in the build.gradle(Module: app) file.

 implementation 'com.google.android.gms:play-services-maps:16.0.0'
  • Above file is fetching the required classes to use google map in our project.

Now in the AndroidManifest.xml file, add the below code structure in <application> tag.

 <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

Now add the internet permission,

 <uses-permission android:name="android.permission.INTERNET" />

Final code for AndroidManifest.xml file is as the below

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

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Step 3. Required Interface

Create a new JAVA class named TaskLoadedCallback.java

Source code for TaskLoadedCallback.java is as the following

public interface TaskLoadedCallback {
    void onTaskDone(Object... values);
}

Step 4. PointParser class

Make a new JAVA class and give it a name like PointParser.java

Add the following source code in PointParser.java

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.PolylineOptions;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PointsParser extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {
   
    TaskLoadedCallback taskCallback;
    String directionMode = "driving";

    public PointsParser(Context mContext, String directionMode) {
        this.taskCallback = (TaskLoadedCallback) mContext;
        this.directionMode = directionMode;
    }

    // Parsing the data in non-ui thread
    @Override
    protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {

        JSONObject jObject;
        List<List<HashMap<String, String>>> routes = null;

        try {
            jObject = new JSONObject(jsonData[0]);
            Log.d("mylog", jsonData[0].toString());
            DataParser parser = new DataParser();
            Log.d("mylog", parser.toString());

            // Starts parsing data
            routes = parser.parse(jObject);
            Log.d("mylog", "Executing routes");
            Log.d("mylog", routes.toString());

        } catch (Exception e) {
            Log.d("mylog", e.toString());
            e.printStackTrace();
        }
        return routes;
    }

    // Executes in UI thread, after the parsing process
    @Override
    protected void onPostExecute(List<List<HashMap<String, String>>> result) {
        ArrayList<LatLng> points;
        PolylineOptions lineOptions = null;
        // Traversing through all the routes
        for (int i = 0; i < result.size(); i++) {
            points = new ArrayList<>();
            lineOptions = new PolylineOptions();
            // Fetching i-th route
            List<HashMap<String, String>> path = result.get(i);
            // Fetching all the points in i-th route
            for (int j = 0; j < path.size(); j++) {
                HashMap<String, String> point = path.get(j);
                double lat = Double.parseDouble(point.get("lat"));
                double lng = Double.parseDouble(point.get("lng"));
                LatLng position = new LatLng(lat, lng);
                points.add(position);
            }
            // Adding all the points in the route to LineOptions
            lineOptions.addAll(points);
            if (directionMode.equalsIgnoreCase("walking")) {
                lineOptions.width(10);
                lineOptions.color(Color.MAGENTA);
            } else {
                lineOptions.width(20);
                lineOptions.color(Color.RED);
            }
            Log.d("mylog", "onPostExecute lineoptions decoded");
        }

        // Drawing polyline in the Google Map for the i-th route
        if (lineOptions != null) {
            //mMap.addPolyline(lineOptions);
            taskCallback.onTaskDone(lineOptions);

        } else {
            Log.d("mylog", "without Polylines drawn");
        }
    }
}

This class will parse the JSON data which we have received from the google.

When we want to draw the route, we need to parse one URL which consist the location (latitude and longitude) of both origin and destination places.

This URL is looking like below

https://maps.googleapis.com/maps/api/directions/json?origin=Disneyland&destination=Universal+Studios+Hollywood&key=YOUR_API_KEY

This URL is just for information, we do not need to add it to our project.

Changing the color of path

By default, we will draw a RED colored path between two markers.

If you want to change it’s color than you should change below lines

 if (directionMode.equalsIgnoreCase("walking")) {
                lineOptions.width(10);
                lineOptions.color(Color.MAGENTA);
            } else {
                lineOptions.width(20);
                lineOptions.color(Color.RED);
  }

Above code is the part of the onPostExecute() method.

Step 5. FetchURL code

Prepare another JAVA class and name of this class should be FetchURL.java

Source Code block for FetchURL.java is looking like the below

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;



public class FetchURL extends AsyncTask<String, Void, String> {
    Context mContext;
    String directionMode = "driving";

    public FetchURL(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    protected String doInBackground(String... strings) {
        // For storing data from web service
        String data = "";
        directionMode = strings[1];
        try {
            // Fetching the data from web service
            data = downloadUrl(strings[0]);
            Log.d("mylog", "Background task data " + data.toString());
        } catch (Exception e) {
            Log.d("Background Task", e.toString());
        }
        return data;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        PointsParser parserTask = new PointsParser(mContext, directionMode);
        // Invokes the thread for parsing the JSON data
        parserTask.execute(s);
    }

    private String downloadUrl(String strUrl) throws IOException {
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(strUrl);
            // Creating an http connection to communicate with url
            urlConnection = (HttpURLConnection) url.openConnection();
            // Connecting to url
            urlConnection.connect();
            // Reading data from url
            iStream = urlConnection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
            StringBuffer sb = new StringBuffer();
            String line = "";
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            data = sb.toString();
            Log.d("mylog", "Downloaded URL: " + data.toString());
            br.close();
        } catch (Exception e) {
            Log.d("mylog", "Exception downloading URL: " + e.toString());
        } finally {
            iStream.close();
            urlConnection.disconnect();
        }
        return data;
    }
}

This class is making the http call to the URL.

In this class, we are also setting the mode of transport.

We’ve set the directionMode=driving in the current application.
The other modes of transport are:

  • driving (default)
  • walking
  • bicycling
  • transit

To change the mode, update the below line

String directionMode = "driving";

Step 6. DataParser class

Time to create a new class and it’s name is like DataParser.java

Code block of DataParser.java is something like the following

import com.google.android.gms.maps.model.LatLng;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DataParser {
    public List<List<HashMap<String, String>>> parse(JSONObject jObject) {

        List<List<HashMap<String, String>>> routes = new ArrayList<>();
        JSONArray jRoutes;
        JSONArray jLegs;
        JSONArray jSteps;
        try {
            jRoutes = jObject.getJSONArray("routes");
            /** Traversing all routes */
            for (int i = 0; i < jRoutes.length(); i++) {
                jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
                List path = new ArrayList<>();
                /** Traversing all legs */
                for (int j = 0; j < jLegs.length(); j++) {
                    jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");

                    /** Traversing all steps */
                    for (int k = 0; k < jSteps.length(); k++) {
                        String polyline = "";
                        polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
                        List<LatLng> list = decodePoly(polyline);

                        /** Traversing all points */
                        for (int l = 0; l < list.size(); l++) {
                            HashMap<String, String> hm = new HashMap<>();
                            hm.put("lat", Double.toString((list.get(l)).latitude));
                            hm.put("lng", Double.toString((list.get(l)).longitude));
                            path.add(hm);
                        }
                    }
                    routes.add(path);
                }
            }

        } catch (JSONException e) {
            e.printStackTrace();
        } catch (Exception e) {
        }
        return routes;
    }


    /**
     * Method to decode polyline points
     * Courtesy : https://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java
     */
    private List<LatLng> decodePoly(String encoded) {

        List<LatLng> poly = new ArrayList<>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((((double) lat / 1E5)),
                    (((double) lng / 1E5)));
            poly.add(p);
        }

        return poly;
    }
}

As the name suggests, this class is parsing the JSON data which google have sent us.

Step 7. Final Words

Now we just need to change two main files. activity_main.xml and MainActivity.java

Source code for activity_main.xml is something like the following

<?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="match_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/mapNearBy"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_below="@+id/rvToolbar"
        android:layout_weight="1" />

    <Button
        android:id="@+id/btnGetDirection"
        android:text="Get Direction"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground" />
</LinearLayout>

First widget is the fragment. We will load the google map in this widget only.

Second one is the button. When the user will click the button, we will draw a polyline between two points.

Now in the MainActivity.java file, write down the following source code lines

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, TaskLoadedCallback {

    private GoogleMap mMap;
    private MarkerOptions place1, place2;
    Button getDirection;
    private Polyline currentPolyline;

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

        getDirection = findViewById(R.id.btnGetDirection);
        getDirection.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");
            }
        });
        //27.658143,85.3199503
        //27.667491,85.3208583
        place1 = new MarkerOptions().position(new LatLng(22.3039, 70.8022)).title("Location 1");
        place2 = new MarkerOptions().position(new LatLng(23.0225, 72.5714)).title("Location 2");
        MapFragment mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.mapNearBy);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        Log.d("mylog", "Added Markers");
        mMap.addMarker(place1);
        mMap.addMarker(place2);

        CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(22.7739,71.6673))
                .zoom(7)
                .bearing(0)
                .tilt(45)
                .build();

        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 5000, null);
    }

    private String getUrl(LatLng origin, LatLng dest, String directionMode) {
        // Origin of route
        String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
        // Destination of route
        String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
        // Mode
        String mode = "mode=" + directionMode;
        // Building the parameters to the web service
        String parameters = str_origin + "&" + str_dest + "&" + mode;
        // Output format
        String output = "json";
        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters + "&key=" + getString(R.string.google_maps_key);
        return url;
    }

    @Override
    public void onTaskDone(Object... values) {
        if (currentPolyline != null)
            currentPolyline.remove();
        currentPolyline = mMap.addPolyline((PolylineOptions) values[0]);
    }
}

Let us understand above code step by step.

Consider the following code

 private GoogleMap mMap;
    private MarkerOptions place1, place2;
    Button getDirection;
    private Polyline currentPolyline;
  • Using above lines, compiler will create objects of various classes like GoogleMap, MarkerOptions etc.

There are below important lines in the onCreate() method.

  place1 = new MarkerOptions().position(new LatLng(22.3039, 70.8022)).title("Location 1");
  place2 = new MarkerOptions().position(new LatLng(23.0225, 72.5714)).title("Location 2");
  MapFragment mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.mapNearBy);
        mapFragment.getMapAsync(this);

First two lines are adding two markers on the google map.

You can change latitude and longitude of source and destination (place1 and place2) using first two lines.

Then compiler will get the mapfragment using the findFragmentById and then it will initialize the google map.

When the google map has finished it’s initialization process, then it will animate the camera using below code

 CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(22.7739,71.6673))
                .zoom(7)
                .bearing(0)
                .tilt(45)
                .build();

        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 5000, null);

You can change some camera parameters like zoom, tilt, target place etc.

When the user clicks the button, compiler will execute the below coding lines

 getDirection.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");
            }
        });

Here, it is the starting point of the process of drawing the path route between markers.

If you have followed above tutorial lines perfectly then you should get the output as you have shown in the output video at the starting of the tutorial.

Possible Errors

When drawing route on google map, there can be two most common errors you may have to face.

First is like This API project is not authorized to use this API.

Look at this below image

Search for “mylog” as per the above image in the logcat.

I was getting this error and it took me 6 hours to solve this. And the problem was that my Directions API was not enable !!

So do not waste your time when you are getting this error and quickly enable Directions API. (This is the prime intention of this blog to help you solve this type of simple errors that might eat your big amount of time)

Second possible error is that “You have exceeded your daily request” like following image

It means that your free trial is over. Either you have to use your credit card to get more requests or simply wait for 24 hours.





7. Android Google Map Draw Path Between Current Location And Destination

Android Google Map Draw Path Between Current Location And Destination in this tutorial.

At the end of this tutorial, you will learn how to draw path as i move starting from my current location using google maps.

First of all, we will fetch the current location in terms of latitude and longitude of the user.

Then using this current location and destination coordinates, we will draw a polyline between these two points on android google map.

Following is the output of this example.

Get your Google API

In order to load google map in android, we need to generate one API key in google developer console.

For this purpose, read “Work At Google Developer Console” part in Google Map Android Tutorial. carefully and follow all it’s steps.

Once you have done above step, you will have one Google API key, which we will add in android studio project in few minutes.

After creating API key, we need to enable the Directions API.

Now, watch the following image

android google map draw path, android google map draw path between current location and destination

At google developer console You can see a navigation icon at upper right corner as per above image. Click on it.

A slider menu will be opened, like the following picture

android google map draw path, android google map draw path between current location and destination
Sliding menu

As per the image, click on APIs option.

System will open the screen like below picture

android google map draw path, android google map draw path between current location and destination
Direction API

When you click on Directions API, system will open the following window

android google map draw path
ENABLE API

Click on the ENABLE button as per above image.

Make sure that you have enabled Direction API otherwise you will not be able to draw a path between two markers.

Now, we have Google API key and we have enable Directions API.

Work At Android Studio

In the android studio, make a fresh new project.

While creating new project, select “Empty Activity” as a default template.

Do not choose Maps Activity at all.

Now go through all the below steps.

Step 1. Adding API key

Time to write Google API key which you have already generated.

Inside res->values->strings.xml file, add the following line

 <string name="google_maps_key">YOUR_GOOGLE_API_KEY</string>

Replace “YOUR_GOOGLE_API_KEY” with your original API which you have already generated.

Step 2. Gradle dependency and Manifest changes

We need to add some dependencies that will help us to reduce complexity.

In the build.gradle(Module: app) file, write down the below code lines

 implementation 'com.google.android.gms:play-services-maps:16.0.0'
 implementation 'com.google.android.gms:play-services-location:16.0.0'
 implementation 'com.karumi:dexter:5.0.0'
  • First line will enable us to draw a google map on our android screen.
  • Second line will give us access to the required classes which will help us to fetch the current location latitude and longitude of the user.
  • Third will allow us to use the dexter library which will simplify the process of runtime permissions in android.

Now in  AndroidManifest.xml file, add the below internet and location permissions.

 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

We also have to add following code inside <application> tag.

 <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
<meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

Final and full code for AndroidManifest.xml file is as the following

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

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">


        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Step 3. An interface

We need one interface in this example. So make a new class and give it a name TaskLoadedCallback.java

Source code for TaskLoadedCallback.java is as the below

public interface TaskLoadedCallback {
    void onTaskDone(Object... values);
}

Step 4. Making PointParser class

Prepare a new class and named it like PointParser.java

PointParser.java class should contain the following code

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.PolylineOptions;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PointsParser extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {

    TaskLoadedCallback taskCallback;
    String directionMode = "driving";

    public PointsParser(Context mContext, String directionMode) {
        this.taskCallback = (TaskLoadedCallback) mContext;
        this.directionMode = directionMode;
    }

    // Parsing the data in non-ui thread
    @Override
    protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {

        JSONObject jObject;
        List<List<HashMap<String, String>>> routes = null;

        try {
            jObject = new JSONObject(jsonData[0]);
            Log.d("mylog", jsonData[0].toString());
            DataParser parser = new DataParser();
            Log.d("mylog", parser.toString());

            // Starts parsing data
            routes = parser.parse(jObject);
            Log.d("mylog", "Executing routes");
            Log.d("mylog", routes.toString());

        } catch (Exception e) {
            Log.d("mylog", e.toString());
            e.printStackTrace();
        }
        return routes;
    }

    // Executes in UI thread, after the parsing process
    @Override
    protected void onPostExecute(List<List<HashMap<String, String>>> result) {
        ArrayList<LatLng> points;
        PolylineOptions lineOptions = null;
        // Traversing through all the routes
        for (int i = 0; i < result.size(); i++) {
            points = new ArrayList<>();
            lineOptions = new PolylineOptions();
            // Fetching i-th route
            List<HashMap<String, String>> path = result.get(i);
            // Fetching all the points in i-th route
            for (int j = 0; j < path.size(); j++) {
                HashMap<String, String> point = path.get(j);
                double lat = Double.parseDouble(point.get("lat"));
                double lng = Double.parseDouble(point.get("lng"));
                LatLng position = new LatLng(lat, lng);
                points.add(position);
            }
            // Adding all the points in the route to LineOptions
            lineOptions.addAll(points);
            if (directionMode.equalsIgnoreCase("walking")) {
                lineOptions.width(10);
                lineOptions.color(Color.MAGENTA);
            } else {
                lineOptions.width(20);
                lineOptions.color(Color.RED);
            }
            Log.d("mylog", "onPostExecute lineoptions decoded");
        }

        // Drawing polyline in the Google Map for the i-th route
        if (lineOptions != null) {
            //mMap.addPolyline(lineOptions);
            taskCallback.onTaskDone(lineOptions);

        } else {
            Log.d("mylog", "without Polylines drawn");
        }
    }
}
  • Google will send us one JSON data, which will be parsed using this class.
  • When we want to draw the route, we need to parse one URL which consist the location (latitude and longitude) of both origin and destination places.

This URL has the below structure

https://maps.googleapis.com/maps/api/directions/json?origin=Disneyland&destination=Universal+Studios+Hollywood&key=YOUR_API_KEY

Color of Path

I am drawing a red color path in this example.

If you want to change the color of this path, you need to update below lines

 if (directionMode.equalsIgnoreCase("walking")) {
                lineOptions.width(10);
                lineOptions.color(Color.MAGENTA);
            } else {
                lineOptions.width(20);
                lineOptions.color(Color.RED);
  }

onPostExecute() method contains the above lines.

Step 5. FetchURL source code

Create a new JAVA class with name FetchURL.java

Source Code block for FetchURL.java is looking like the below

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;


public class FetchURL extends AsyncTask<String, Void, String> {

    Context mContext;
    String directionMode = "driving";

    public FetchURL(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    protected String doInBackground(String... strings) {
        // For storing data from web service
        String data = "";
        directionMode = strings[1];
        try {
            // Fetching the data from web service
            data = downloadUrl(strings[0]);
            Log.d("mylog", "Background task data " + data.toString());
        } catch (Exception e) {
            Log.d("Background Task", e.toString());
        }
        return data;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        PointsParser parserTask = new PointsParser(mContext, directionMode);
        // Invokes the thread for parsing the JSON data
        parserTask.execute(s);
    }

    private String downloadUrl(String strUrl) throws IOException {
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(strUrl);
            // Creating an http connection to communicate with url
            urlConnection = (HttpURLConnection) url.openConnection();
            // Connecting to url
            urlConnection.connect();
            // Reading data from url
            iStream = urlConnection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
            StringBuffer sb = new StringBuffer();
            String line = "";
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            data = sb.toString();
            Log.d("mylog", "Downloaded URL: " + data.toString());
            br.close();
        } catch (Exception e) {
            Log.d("mylog", "Exception downloading URL: " + e.toString());
        } finally {
            iStream.close();
            urlConnection.disconnect();
        }
        return data;
    }
}
  • We will make the http call using the above class.
  • This class also holds the mode of transporat.
  • Right now the mode is “driving.
  • Possible values for mode is driving, bicycling, transit and walking.

To update the mode, update the below line

String directionMode = "driving";

Step 6. Class of DataParser

Again make a new class and give it a name like DataParser.java

Following is the code structure for DataParser.java

import com.google.android.gms.maps.model.LatLng;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DataParser {
    public List<List<HashMap<String, String>>> parse(JSONObject jObject) {

        List<List<HashMap<String, String>>> routes = new ArrayList<>();
        JSONArray jRoutes;
        JSONArray jLegs;
        JSONArray jSteps;
        try {
            jRoutes = jObject.getJSONArray("routes");
            /** Traversing all routes */
            for (int i = 0; i < jRoutes.length(); i++) {
                jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
                List path = new ArrayList<>();
                /** Traversing all legs */
                for (int j = 0; j < jLegs.length(); j++) {
                    jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");

                    /** Traversing all steps */
                    for (int k = 0; k < jSteps.length(); k++) {
                        String polyline = "";
                        polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
                        List<LatLng> list = decodePoly(polyline);

                        /** Traversing all points */
                        for (int l = 0; l < list.size(); l++) {
                            HashMap<String, String> hm = new HashMap<>();
                            hm.put("lat", Double.toString((list.get(l)).latitude));
                            hm.put("lng", Double.toString((list.get(l)).longitude));
                            path.add(hm);
                        }
                    }
                    routes.add(path);
                }
            }

        } catch (JSONException e) {
            e.printStackTrace();
        } catch (Exception e) {
        }
        return routes;
    }


    /**
     * Method to decode polyline points
     * Courtesy : https://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java
     */
    private List<LatLng> decodePoly(String encoded) {

        List<LatLng> poly = new ArrayList<>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((((double) lat / 1E5)),
                    (((double) lng / 1E5)));
            poly.add(p);
        }

        return poly;
    }
}

Step 7. Main Modifications

Now final thing is to update activity_main.xml and MainActivity.java files.

In the activity_main.xml file, add the below lines

<?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="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tv"
        android:textSize="18sp"
        android:textColor="#000"/>

    <fragment
        android:id="@+id/mapNearBy"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_below="@+id/rvToolbar"
        android:layout_weight="1" />

    <Button
        android:id="@+id/btnGetDirection"
        android:text="Get Direction"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground" />
</LinearLayout>
  • Three widgets are there in this main layout file.
  • First is text view which will hold the value of the current latitude and longitude.
  • fragment will allow us to load google map inside itself.
  • When the user will click the button, we will draw the line between current location and destination on google map.

Now source code for MainActivity.java is as the below

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.DexterError;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.PermissionRequestErrorListener;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import java.util.List;

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener , OnMapReadyCallback, TaskLoadedCallback{

   //variables for map and route

    private GoogleMap mMap;
    private MarkerOptions place1, place2;
    Button getDirection;
    private Polyline currentPolyline;
    private MapFragment mapFragment;
    private boolean isFirstTime = true;

//variables for current location
    private static final String TAG = "MainActivity";

    private TextView tvLocation;
    private GoogleApiClient mGoogleApiClient;
    private Location mLocation;
    private LocationRequest mLocationRequest;
    private com.google.android.gms.location.LocationListener listener;
    private long UPDATE_INTERVAL = 2 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 2000; /* 2 sec */

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

        //code for getting current location
        requestMultiplePermissions();

        tvLocation = (TextView) findViewById((R.id.tv));

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();


    }

    //code for drawing route

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        mMap.clear();
        Log.d("mylog", "Added Markers");
        mMap.addMarker(place1);
        mMap.addMarker(place2);

        CameraPosition googlePlex = CameraPosition.builder()
                .target(new LatLng(22.7739,71.6673))
                .zoom(7)
                .bearing(0)
                .tilt(45)
                .build();

        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(googlePlex), 5000, null);
    }

    private String getUrl(LatLng origin, LatLng dest, String directionMode) {
        // Origin of route
        String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
        // Destination of route
        String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
        // Mode
        String mode = "mode=" + directionMode;
        // Building the parameters to the web service
        String parameters = str_origin + "&" + str_dest + "&" + mode;
        // Output format
        String output = "json";
        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters + "&key=" + getString(R.string.google_maps_key);
        return url;
    }

    @Override
    public void onTaskDone(Object... values) {
        if (currentPolyline != null)
            currentPolyline.remove();
        currentPolyline = mMap.addPolyline((PolylineOptions) values[0]);
    }

    //runtime permission method

    private void  requestMultiplePermissions(){
        Dexter.withActivity(this)
                .withPermissions(
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION )
                .withListener(new MultiplePermissionsListener() {
                    @Override
                    public void onPermissionsChecked(MultiplePermissionsReport report) {
                        // check if all permissions are granted
                        if (report.areAllPermissionsGranted()) {
                            Toast.makeText(getApplicationContext(), "All permissions are granted by user!", Toast.LENGTH_SHORT).show();
                        }

                        // check for permanent denial of any permission
                        if (report.isAnyPermissionPermanentlyDenied()) {
                            // show alert dialog navigating to Settings
                            openSettingsDialog();
                        }
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).
                withErrorListener(new PermissionRequestErrorListener() {
                    @Override
                    public void onError(DexterError error) {
                        Toast.makeText(getApplicationContext(), "Some Error! ", Toast.LENGTH_SHORT).show();
                    }
                })
                .onSameThread()
                .check();
    }

    private void openSettingsDialog() {

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("Required Permissions");
        builder.setMessage("This app require permission to use awesome feature. Grant them in app settings.");
        builder.setPositiveButton("Take Me To SETTINGS", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, 101);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.show();

    }


   //methods for getting current location

    @Override
    public void onConnected(Bundle bundle) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }

        startLocationUpdates();

        mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

        if(mLocation == null){
            startLocationUpdates();
        }
        if (mLocation != null) {

            // mLatitudeTextView.setText(String.valueOf(mLocation.getLatitude()));
            //mLongitudeTextView.setText(String.valueOf(mLocation.getLongitude()));
        } else {
            Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(TAG, "Connection Suspended");
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode());
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    protected void startLocationUpdates() {
        // Create the location request
        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL)
                .setFastestInterval(FASTEST_INTERVAL);
        // Request location updates
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
                mLocationRequest, this);
        Log.d("reque", "--->>>>");
    }

    @Override
    public void onLocationChanged(Location location) {

        String msg = "Updated Location: " +
                Double.toString(location.getLatitude()) + "," +
                Double.toString(location.getLongitude());

        tvLocation.setText(String.valueOf(location.getLatitude() +"    "+String.valueOf(location.getLongitude())));
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();

        if(isFirstTime){
            //code to draw path on map

            getDirection = findViewById(R.id.btnGetDirection);
            getDirection.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");
                }
            });

            place1 = new MarkerOptions().position(new LatLng(location.getLatitude(), location.getLongitude())).title("Location 1");
            place2 = new MarkerOptions().position(new LatLng(20.8880, 70.4012)).title("Location 2");
            mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.mapNearBy);
            mapFragment.getMapAsync(this);
            isFirstTime = false;
        }

     }

}

Let us dive deep in the above source code.

  • First of all, compiler will create some variables and objects of various classes like GoogleMap, MarkerOptions, Polyline, Location, LocationRequest etc.
  • Some of them are for google map and route and some will help to fetch current location.

Now read the onCreate() method.

  • Here, compiler will first call the requestMultiplePermissions() method.
  • This method will ask for runtime permissions.
  • Then compiler will initialize textview and mGoogleApiClient.
  • Compiler will call onMapReady() function when map has finished it’s loading.
  • getUrl() and onTaskDone() methods are the part of drawing the route process.
  • onConnected(), onConnectionSuspended(), onConnectionFailed(), onStart(), onStop() and startLocationUpdates() methods are the part of getting the current location.

Now read the onLocationChanged() method.

  • In this method, we will get current latitude and longitude.
  • Compiler will set the value of latitude and longitude in text view and then it will show one toast.
  • After that, it will check one if condition. Compiler will go inside this if condition only once.
  • Inside if condition, compiler will set the code for button’s click event.

Following line will draw the path.

 new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");

Then compiler will execute the map loading process.

How to draw path when user is moving

Here, we are drawing the path on the user click only.

If you want to draw path with the movement of the user then simply write below line inside onLocationChanged() method instead of writing it inside button click.

Source code for this may be looking like

@Override
    public void onLocationChanged(Location location) {

 place1 = new MarkerOptions().position(new LatLng(location.getLatitude(), location.getLongitude())).title("Location 1"); 
place2 = new MarkerOptions().position(new LatLng(20.8880, 70.4012)).title("Location 2"); mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.mapNearBy);
 mapFragment.getMapAsync(this);

 new FetchURL(MainActivity.this).execute(getUrl(place1.getPosition(), place2.getPosition(), "driving"), "driving");

}

Above code will constantly draw the line as per the movement of the user.

May be you get some errors

There are possibility for two types of errors in this example.

First is something like “This API project is not authorized to use this API“.

See the following image


Search for “mylog” in logcat as per above picture.

I was getting this error and it took me 6 hours to solve this. And the problem was that my Directions API was not enable !!

So do not waste your time when you are getting this error and quickly enable Directions API. (This is the prime intention of this blog to help you solve this type of simple errors that might eat your big amount of time)

Second possible error is that “You have exceeded your daily request” like following picture


This error is saying that your free quota for today is over. So you need to wait for 24 hours or you can create a new API to go further.