Android Google Maps API V2 Tutorial


In many situations, we may need to display a map in our Android app, plotting some specific locations that are geographically located in different places. In this case, Google has made our tasks much easier. The Google Maps API and Android’s location-based services together form a strong and interesting platform to get integrated with our app.

The location-based features in Android are two-fold:

  • The Google Maps API V2

  • Android location-based API

The mapping APIs in Android provides means to display a map on the screen and to interact with it. For example, a user can zoom and pan the map, and can add customized location data (that we will see later) onto the map. On the other hand, the location API enables us to track device location either with Global Positioning System (GPS) or with GSM network, or with both.

Tutorial Organization

This tutorial is divided into two parts:

  • Displaying a map in our Android app

  • Extract location data within the app and show them on the map

Development Environment

I have used Android Studio (version 1.2.2) as the development environment, under Windows 7 operating system, to write, debug and test the program that is used for demonstration in this tutorial. Also, I have tested the program on a real device, not in an emulator.

Part-1: Display a Map in the App

Objective

In this first part of the tutorial, our objective is to be able to write an app that can display a map on a device screen.

The Very First Step

The very first step to begin working with Google map and location API in Android is to show a map on the display. To do so, we have to obtain a Maps API key from Google. The Maps API key enables our Android app to interact with Google Maps services to get map data.

What is API Key

Since our application is going to interact with Google maps server, it has to be done via the API (Application Programming Interface) created by Google to interact applications (Android applications, Microsoft C# applications or anything else) with their server. To identify which program or application is calling its API, and who is its developer, Google has created provisions to create an API Key in this regard, to identify the calling program, its developer, or its user who is accessing their server. You will find not only Google but also other websites like Facebook, to provide API keys to access their server via applications. This is done primarily to:

  • Authenticate the Application

  • Prevent malicious use or abuse of the API

Obtaining a Maps API Key from Google

To get a Maps API key, we need the certificate that is used to sign the apps. First we have to locate the key store file that our development environment uses in this regard. In this tutorial, we will use Android Studio as the IDE, running under Windows 7 operating system where the user is Administrator. In this environment, the following file is used to sign the apps:

C:\Users\ Administrator\.android\debug.keystore

Now open command prompt and write the following command to generate an SHA-1 fingerprint of the debug certificate:

keytool -list -alias androiddebugkey -keystore C:\Users\Administrator\.android\debug.keystore -storepass android -keypass android

Note that the alias we want from the debug key is androiddebugkey. Also, note that both of the keystore password and private key password is android. When we run this command, the keytool generates the SHA-1 fingerprint. In our case it is:

64:A9:37:FB:18:C7:63:5A:49:E5:A6:55:1C:18:9F:F3:47:19:E6:43

Please see screenshot-1 for details.

(Screenshot-1)

Now go to the following site:

http://code.google.com/android/maps-api-signup.html

And follow these steps:

  1. Click on GET A KEY button and select CONTINUE the next screen

  2. The Gmail login screen will appear. Provide your login credentials there.

  3. In the Google Developers Console, from the dropdown list, select the default project and below check the “I agree to the terms of use” checkbox. Press Agree and continue.

  4. In the screen next, provide the SHA-1 fingerprint of the debug keystrore that we created earlier. In this case it is:

64:A9:37:FB:18:C7:63:5A:49:E5:A6:55:1C:18:9F:F3:47:19:E6:43

  1. In the next screen, you will be given the API key that you have been looking for. Note this API key, and preserve it for future use.

Prepare the Development Environment

Now we are going to prepare our development environment for writing map-based applications. We have to add Google Play services package in our SDK in this regard

Install Google Play Services

Let’s open Android Studio and go to SDK Manager. Under the Extras section, check whether Google Play services package is installed or not (see screenshot-2). If not, check it, and click install package (s). Accept license agreements and proceed through the automatic installation process.

(Screenshot-2)

Display Map on the Screen

Now that our entire development environment is set up, the next step that we are going to take is displaying a map in our app. To do so, let’s follow below steps.

Create a New Google Map Project

Opening a Google map project is slightly different from ordinary ones. In Android Studio:

  • Go to File->New->New Project

  • In New Project screen:

    • Application Name: My Map Location Application

    • Company Domain: mymaplocation.org

    • Package name (automatic): org.mymaplocation.mymaplocationapplication

  • Click Next

  • Select Phone and Tablet in Target Android Devices screen and click Next

  • In Add an activity to Mobile screen, select Google Maps Activity and click Next

  • Leave the fields to default values in the Customize the Activity screen, and click Finish

Our new Android Studio project for Google Maps is now created.

The Project Structure

Our newly created project consists of components similar to ordinary Android Studio projects except the google_maps_api.xml file under res->values location. The purpose of this file is to store the Google Maps API key for our project. Besides, In our app, we will work with following files:

  • activity_maps.xml file under res->layout location. It will hold the map fragment to display the map.

  • MapsActivity.java file under app->java location. We will customize the app and extract location data here.

  • AndroidManifest.xml file under apps->manifests location. We will set individual permissions that our app needs, to fetch location data over the internet and display them on the screen.

 

The XML Layout File

By default, the XML file that defines the app's layout is at res/layout/activity_maps.xml. It contains the following code:

(activity_maps.xml)

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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" />

 

This layout makes use of Android’s SupportMapFragment class. This fragment is the simplest way to place a map in an application. It's a wrapper around a view of a map to automatically handle the necessary life cycle needs.

We are not going to make any change to this default code.

Set up the Maps API Key

We will statically assign the Google Maps API key that we obtained earlier in this tutorial in our project’s google_maps_api.xml file. Simply place your API key in between the <string /> element in the file. The resultant file will look like following:

 

(google_maps_api.xml)

<resources>
<string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
YOUR_MAPS_API_KEY_HERE
</string>
</resources>

Update the Manifest File

In the application manifest file of our project (AndroidManifest.xml), we will specify some permission (required from host operating system) related to network and Internet access. The following permissions are required (few are recommended):

  • android.permission.INTERNET

  • android.permission.ACCESS_NETWORK_STATE

  • android.permission.WRITE_EXTERNAL_STORAGE

  • android.permission.ACCESS_COARSE_LOCATION

  • android.permission.ACCESS_FINE_LOCATION

  • com.google.android.providers.gsf.permission.READ_GSERVICES

 

After adding the above permission requests, the final manifest file should look like following:

(AndroidManifest.xml)

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

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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.maps.v2.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>

The MapsActivity

Upon creation of the project in Android Studio, the MapsActivity.java file initially contains the following code. Make sure it matches your code, too. Otherwise, you may face difficulties proceeding through the next phases of the tutorial.

(MapsActivity.java)

package org.mymaplocation.mymaplocationapplication;

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;

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

public class MapsActivity extends FragmentActivity {

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.

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

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                    .getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    // Add a marker on the map near Africa.
    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }
}

From above code we observe that:

  • The activity is structured similarly to other Android activities that implement the onCreate() and onPause() lifecycle methods in its way.

  • The setUpMapIfNeeded() method gets the instance of the support map fragment associated with our app’s layout file.

  • The setUpMap() method simply adds a marker on the map at latitude=0 and longitude=0 position on the Earth which is located somewhere near Africa. The method also sets the marker title as “Marker” that will be shown if we tap on the marker.

For the time being, we are not going to make any change to this file. In the next part of this tutorial, we are going to fetch custom location data within this activity with the help of Android location-based API.

Running the App

Now that we have done the necessary things let’s run our simple app that displays a map on the device screen. I have used an Android device to run the app. Please see screenshot-3 below.

(Screenshot-3)

Extra: Customizing the Map

Now that we have a working map application in our hand let’s do some simple customizations on it. We will do the following customizations:

  • Change the map marker icon

  • Change the map type

Note that source code of this section is for simple modifications to the map only. Any changes made to the source code presented in this section will not be carried out to the final source code.

Change the map marker icon

To change the map marker icon, let’s first copy the following image that we will use as a marker icon:

(blue_point.png)

Now go to Android Studio, expand app->res->drawable location and right mouse click on it; choose Copy Path from the menu. This path is where your drawable folder is located on your computer. Save the above image to that location with the same name above.

Now go to MapsActivity.java file in Android studio, and replace existing code inside setUpMap() method with the following code:

mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.blue_point))
.snippet("Your " + "location is here"));

It clearly shows that we appended some new portions after setting the marker title:

  • Set the icon with icon method using the image that we created earlier

  • Set a snippet for the marker that will be shown if we tap on the marker icon.

If we run the app now on a device at this stage, we will see output like the following screenshot-4. If we tap on the marker icon, it will show the marker title and the snippet that we have set (screenshot-5).

(Screenshot-4)                                                                                 (Screenshot-5)

Change the map type

In our app so far, we have not set or changed the map type programmatically. By default, it is set to the normal map type. The Google Maps API for Android offers four types of maps, as well as an option to have to map at all. These are:

  1. Normal: This is the basic map type with roads, some man-made features, and important natural features such as rivers are shown. Road and features labels are also visible.

  2. Satellite: In this map type, satellite photograph data is shown along with road and feature labels visible.

  3. Hybrid: As the name suggests, it is a combination of normal and hybrid map types. Here satellite photograph data is shown with road maps added. Road and feature labels are also visible.

  4. Terrain: In this map type topographic data is shown. Some roads and labels are also visible.

  • None: If we specify a map with map type none, the map will be loaded as an empty grid with no tiles shown.

Now let’s change the type of the map that is being displayed in our app. Since we have already seen how the normal map type looks like, we will now see the satellite type map. To do so, let’s go back to the setUpMap() method of our MapsActivity again. Add the following code at the end of the method.

mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

If we run the app now, we will see that our map has been set to render satellite photograph data fetched by Google’s satellites (screenshot-6). After that, change the argument in above setMapType() method to:

  • GoogleMap.MAP_TYPE_HYBRID to obtain hybrid map type (screenshot-7)

  • GoogleMap.MAP_TYPE_TERRAIN to obtain terrain map type (screenshot-8)

(Screenshot-6)                                              (Screenshot-7)                                               (Screenshot-8)

 

Part-2: Display Location Data on Map

Objective

In this second and final part of the tutorial, our objective is to simulate the behavior of a GPS Tracker that can track a user’s location, even on the go. We are going to plan this part as following:

  • We are going to use the foundation that we have created in Part-1 of this tutorial. We can create a Google Map and can place a marker on it, pointing to a location on the earth specified by a (latitude, longitude) pair.

  • We will track a user’s current location with the help of Android’s location API. Beginning at that point, we will periodically change the location position programmatically.

  • We will track the change and display the movement on the map.

Obtain Location Data

We are going to edit/update the MapActivity.java file to obtain location data within our app. Let’s follow the steps mentioned below.

  1. In the variable declarations part, declare following private variables after the private GoogleMap mMap declaration:

private Marker mMarker;

final Handler handler = new Handler();

private LocationManager locMan;
private Location lastLoc;
private static double lat;
private static double lng;

Note: After adding above code in the IDE, Android Studio will automatically import required packages (your IDE should be set up to do so). Only there will be a red error mark in the Handler class because there are two packages that can be imported. You should import the android.os.Handler package.

  1. In onCreate() method, before the setupMapIfNeeded() method call, place the code mentioned below:

locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
try {
   lastLoc = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
   lat = lastLoc.getLatitude();
   lng = lastLoc.getLongitude();
} catch (Exception e) {
   lat = 0.0;
   lng = 0.0;
}

Explanation

Explanations to the above steps are given below:

  • In step (i) above:

    • The variable mMarker will be used to instantiate the marker on the map. Note that in the example of Part-1 we did not instantiate the marker. In Part-2, however, we need to check for null, and that’s why we are going to instantiate it.

    • The handler variable will be used to periodically check and update user’s location at a regular interval (3000 milliseconds).

    • The locMan variable is used to manage access to the system’s location based services.

    • The lastLoc variable holds user’s last location information that was tracked by the system, and the lat and lng variables hold corresponding latitude and longitude information.

  • In step (ii) above we get user’s location with the help of the location manager and extract corresponding latitude and longitude values. The values are set to default values (0, 0) if an exception is thrown.

Checking Your Current Location

If you run the app at this stage and navigate to your current location, you will see a marker on the map pointing to your current location (screenshot-9).

(Screenshot-9)

Simulate User’s Movement

Now we will make a simulation beginning at your current location so that it seems that you are moving from your current place to another place, and the marker on the map will follow this position accordingly. For this purpose, we are going to make use of the handler variable that we created earlier. This variable will be used to check/update user’s location data, and this data will be displayed on the map.

Let’s follow the steps mentioned below.

  1. Add the following class definition, just before the MapsActivity class ending curly brace:

private class A implements Runnable {
    public void run() {
        // Do your Task
        setUpMap();
        handler.postDelayed(this, 3000);
    }
}

As we see above, class A implements Android’s Runnable interface. Therefore, it creates a separate thread. It checks for user’s latest location information at a 3000 milliseconds interval and updates the map accordingly (by calling setUpMap() method).

  1. In setUpMapIfNeeded() method, in if (mMap != null) block, replace the existing code with the following code:

A a = new A();
handler.postDelayed(a, 500);

The above code creates an instance of the class A created in step (i) and instructs it to run after 500 milliseconds.

  1. In setUpMap() method, replace the whole code with the following code:

if (mMarker != null) { // Marker already exists. Remove it first.
   mMarker.remove();
   // Increment values by 0.01
   lat = lat + 0.1;
   lng = lng + 0.1;
}

// Draw a marker with given lat and lng values
mMarker = mMap.addMarker(new MarkerOptions().
position(new LatLng(lat, lng)).title("You are here!"));


mMap.animateCamera(CameraUpdateFactory.
newLatLng(new LatLng(lat, lng)), 3000, null);

The code above checks whether a marker object is already created. If so, it removes it, increases the latitude and longitude values. Then a new marker is created pointing to the updated location, and the map is animated towards the current marker position.

That’s it! We have written the last line of code of this tutorial. Now we are going to check whether it works or not.

Complete Source Code of MapsActivity.java

Thus, the complete source code of the MapsActivity.java file is given below:

(MapsActivity.java)

package org.mymaplocation.mymaplocationapplication;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
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 {

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
    private Marker mMarker;

    final Handler handler = new Handler();

    private LocationManager locMan;
    private Location lastLoc;
    private static double lat;
    private static double lng;

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

        // Get user location.
        locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        try {
            lastLoc = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            lat = lastLoc.getLatitude();
            lng = lastLoc.getLongitude();
        } catch (Exception e) {
            lat = 0.0;
            lng = 0.0;
        }

        setUpMapIfNeeded();

    }

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                A a = new A();
                handler.postDelayed(a, 500);
            }
        }
    }

    private void setUpMap() {
        if (mMarker != null) { // Marker already exists. Remove it first.
            mMarker.remove();
            // Increment values by 0.01
            lat = lat + 0.1;
            lng = lng + 0.1;
        }
        // Draw a marker with given lat and lng values
        mMarker = mMap.addMarker(new MarkerOptions().position(new LatLng(lat, lng)).title("You are here!"));
        mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(lat, lng)), 3000, null);

    }

    private class A implements Runnable {

        public void run() {
            // Do your Task
            setUpMap();
            handler.postDelayed(this, 3000);
        }
    }
}

Checking the Simulation

If we run our app at this final stage we will observe following things:

  • A map is displayed on the screen, initially displaying a place near Africa (0,0 point)

  • The map slowly animates to the user’s current location

  • A marker pointing at user’s current location is displayed on the map

  • At every 3 seconds’ interval, the marker is changing its position to the new simulated location

Three screenshots of the app running at three instances of time are given below.

Points to be noted

  • You have to enable location data on your device to successfully run this app.

  • We have used a change of 0.1 degrees both in latitude and longitude. It refers to a change in around 1km distance in the 3 sec time delay specified in handler.postDelayed() method. Although not so realistic, these values are used to make the simulation (animation) clearly visible to the user.

  • The simulation is more realistic if the increment values of latitude and longitude are made smaller and the map is zoomed out to user’s location.

  • If a user is moving, then if we remove following two lines from the setUpMap() method, we will obtain a real GPS Tracker!

lat = lat + 0.1;
lng = lng + 0.1;