Integrating Google Maps into an Android Application

Setup

Implementing Google Maps is more involved than Google Analytics. Unlike Google Analytics, where you copy a jar file to your lib directory, you must do the following before you can begin using Google Maps in your app:

  1. Download the Google Play Services SDK by launching the SDK manager and selecting the Google Play services under Extras

  2. Import the Google Play Services SDK into your workspace
    • Within Eclipse, go to File -> Import and select Existing Android Code into Workspace
    • Click the Browse button and navigate to the <sdk-dir>/extras/google/google_play_services/libproject/google-play-services_lib
    • Check the Copy projects into workspace box.
    • Click Finish
  3. Make the imported project a library
    • Right click on the project and go down to Properties
    • In the left pane, click on the leaf called Android.
    • Check the box that says is Library
  4. Add a library reference
    • Right click on the project that will use Google Maps and go down to Properties
    • In the left pane, click on the leaf called Android.
    • Within the Library section, click the Add button and select the Google Play services SDK project.
    • Click OK to close the dialog window.
  5. Add the following to the AndroidManifest.xml file:
    <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    
  6. Get an Android certificate's SHA1 fingerprint; a debug/developer certificate fingerprint will work for testing but you will not be able to upload your app with a debug/developer certificate. A debug/developer certificate is automatically generated when using Eclipse for debugging. To retrieve the debug/developer SHA1 fingerprint:
    • Open Eclipse's preferences
    • Click on the Build leaf
    • Copy the SHA1 fingerprint

    Another way of obtaining the information you need is by running the following command:
    keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
    keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
  7. Get a Google Maps API key which can be obtained by
    • Logging into Google's Developers Console
    • Select a project
    • Clicking on APIs and auth
    • Set the status of Google Maps Android API v2 to On(Only necessary if you haven't enabled it already.)
    • Click Credentials
    • Click CREAT NEW KEY and enter the SHA1 finger print followed by a semicolon (;) and package name. For example if my package name is com.paulusworld.androidmap, I would enter something like 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00;com.paulusworld.androidmap
  8. Add the API key to the AndroidManifest.xml within the <application> tag file
    <meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value="YOUR_API_KEY_HERE"/>
    
  9. Add the following permissions to the AndroidManifest.xml file:
    <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" />
    <!--
    The following two permissions are not required to use
    Google Maps Android API v2, but are recommended.
    -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
  10. Since Goole Maps v2 uses OpenGL ES 2, if it's not installed on the phone, then it will not work. Add the following to the AndroidManifest.xml within the <application> tags:
    <uses-feature
         android:glEsVersion="0x00020000"
         android:required="true" />

Testing

If you imported Google Play services and configured your project properly, the following example will work. Create a new Android project.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.paulusworld.androidmaps"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

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

    <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" />
    <!--
     The following two permissions are not required to use
     Google Maps Android API v2, but are recommended.
    -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.paulusworld.androidmaps.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <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="YOUR_API_KEY_HERE" />
    </application>

</manifest>

 

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

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

</LinearLayout>

 

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;

import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

    /**
     * Note that this may be null if the Google Play services APK is not available.
     */
    private GoogleMap mMap;

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

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

    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }

}

The above code was slightly modified from the example I obtained from the Google Maps documentation.

Using Google Maps

Below, I've modified the setUpMap function to search for "Milwaukee Public Museum". The first result is being used to get the coordinates, place a marker on the map, and zoom in far enough to show the floor picker.

private void setUpMap() {
		Geocoder gc = new Geocoder(this);
		List<Address> results;
		try {
			// Search for "Milwaukee Public Museum" and only except 1 search result.
			results = gc.getFromLocationName("Milwaukee Public Museum", 1);
			Address aMPM = results.get(0);
// The getFeatureName() function returns the name of the place, which is // Milwaukee Public Museum. mMap.addMarker(new MarkerOptions().position( new LatLng(aMPM.getLatitude(), aMPM.getLongitude())).title( aMPM.getFeatureName()));
// Centers the camera over the building and zooms int far enough to // show the floor picker. mMap.moveCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(results.get(0).getLatitude(), results.get(0) .getLongitude()), 18)); } catch (IOException e) { // Normally we'd do more here, but this is an example. } }

Another thing you may want to do is display a Google Map along with some additional content. Here we have a map that is about half the hieight of the screen with three TextViews.

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

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="45"
        class="com.google.android.gms.maps.SupportMapFragment" />

    <TextView
        android:id="@+id/textViewLocation1"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="15"
        android:background="#0099FF"
        android:textColor="#000000" />

    <TextView
        android:id="@+id/textViewLocation2"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="15"
        android:background="#00CCFF"
        android:textColor="#000000" />

    <TextView
        android:id="@+id/textViewDragging"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="15"
        android:background="#00FFFF"
        android:textColor="#000000" />
</LinearLayout>

Below will move the camerea to the location specified by lat and long. The third parameter tells the camera how far to zoom in. A value of 18 will display the indoor maps. However, it's not currently possible to select a floor through code.

mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, long, zoom));
mMap.addMarker(new MarkerOptions()
		.position(new LatLng(lat, long)) // Position of where the marker should be placed
		.title("Location 1") // The title of the marker. This will be displayed on the first line with in the bubble
		.snippet("Snippet") // Additional content that will be added to the bubble.
		.draggable(true)); // Allows the marker to be moved.

You can find all the MarkerOptions methods in the API.

OnMarkerClickListener

There are two ways of doing this, the first is as follows:

public class MainActivity extends FragmentActivity implements OnMarkerClickListener {
	private GoogleMap mMap;
	private Marker mLocation1;

	// snipp

	private void setUpMap() {
		mLocation1 = mMap.addMarker(new MarkerOptions()
				.position(new LatLng(lat, long))
				.title("Location 1")
				.snippet("Snippet")
				.draggable(true));

		mMap.setOnMarkerClickListener(this);
		// Snipped more code
	}

	@Override
	public boolean onMarkerClick(Marker marker) {
		// Do something.
	}

	// snipp
}

The mMap variable is the the Google Map object, and the mLocation1 is a marker that will be added to the map when the app runs. You implement the OnMarkerClickListener class and add the onMarkerClick method which handles the click of any marker. The second way you can add an OnMarkerClickListener is as follows:

mMap.setOnMarkerClickListener(new OnMarkerClickListener() {

	@Override
	public boolean onMarkerClick(Marker marker) {
		// TODO Auto-generated method stub
		return false;
	}

});

OnMarkerDragListener

If you set up your markers to be draggable, you can add a listener to perform an action when a user starts, is, and stops dragging a marker.

public class MainActivity extends FragmentActivity implements OnMarkerDragListener {
	private GoogleMap mMap;
	private Marker mLocation1;

	// snipp

	private void setUpMap() {
		mLocation1 = mMap.addMarker(new MarkerOptions()
				.position(new LatLng(lat, long))
				.title("Location 1")
				.snippet("Snippet")
				.draggable(true));

		mMap.setOnMarkerDragListener(this);
		// Snipped more code
	}

	@Override
	public void onMarkerDrag(Marker marker) {
		// do something while dragging

	}

	@Override
	public void onMarkerDragEnd(Marker marker) {
		// do something when the user stops dragging

	}

	@Override
	public void onMarkerDragStart(Marker marker) {
		// do something when the user starts dragging

	}

	// snipp
}

As in the OnMarkerClickListener example, you can also accomplish this by:

mMap.setOnMarkerDragListener(new OnMarkerDragListener() {

	@Override
	public void onMarkerDrag(Marker arg0) {
		// do something while dragging

	}

	@Override
	public void onMarkerDragEnd(Marker arg0) {
		// do something when the user stops dragging

	}

	@Override
	public void onMarkerDragStart(Marker arg0) {
		// do something when the user starts dragging

	}

});

OnMapClickListener

You can add an OnMapClickListener to add markers at the location where a user has clicked on the map or perform another action.

public class MainActivity extends FragmentActivity implements OnMapClickListener {
	private GoogleMap mMap;

	// snipp

	private void setUpMap() {
		mLocation1 = mMap.addMarker(new MarkerOptions()
				.position(new LatLng(lat, long))
				.title("Location 1")
				.snippet("Snippet")
				.draggable(true));

		mMap.setOnMapClickListener(this);
		// Snipped more code
	}

	@Override
	public void onMapClick(LatLng loc) {
		mMap.addMarker(new MarkerOptions()
				.position(loc));
	}

	// snipp
}