Location API and Google Maps in Android

advertisement
Location API and Google Maps in Android - Tutorial
Lars Vogel
Version 3.5
Copyright © 2010 , 2011, 2012 Lars Vogel
18.03.2012
Revision History
Revision 0.1
15.06.2010
Lars
Vogel
18.06.2010 - 18.03.2012
Lars
Vogel
created
Revision 0.2 - 3.5
bugfixes and enhancements
Android Location API and Google Maps
This tutorial describes the usage of the Android Location API, the usage of Google Maps and the
Geolocation API. It is based on Eclipse 3.7, Java 1.6 and Android 4.0 (Ice Cream Sandwich).
Table of Contents
1. Android Basics
2. Android Location API
2.1. Determine the current geolocation
2.2. LocationManager
2.3. LocationProvider
2.4. Selecting LocationProvider via Criteria
2.5. Proximity Alert
2.6. Forward and reverse Geocoding
2.7. Security
2.8. Prompt the user to Enabled GPS
3. Using GPS and setting the current location
3.1. Activating GPS on the emulator
3.2. Setting the geoposition
4. Tutorial: Using the Android Location API
4.1. Create Project
4.2. Add permissions
4.3. Activity
4.4. Run and Test
5. Google Maps
5.1. MapsView
5.2. MapsActivity
5.3. MyLocationOverlay
5.4. ItemizedOverlay and OverlayItems
5.5. Getting the Google Map key
5.6. Device with Google API
6. Tutorial: Google Maps
6.1. Create Project
6.2. Validate AndroidManifest.xml
6.3. ItemizedOverlay
6.4. Layout
6.5. Activity
6.6. Run and Test
7. Thank you
8. Questions and Discussion
9. Links and Literature
9.1. Source Code
9.2. Android Resources
9.3. vogella Resources
1. Android Basics
The following assumes that you have already basic knowledge in Android development. Please
check the Android development tutorial for the basics.
2. Android Location API
2.1. Determine the current geolocation
Most Android devices allow to determine the current geolocation. This can be done via a GPS
(Global Positioning System) device, via cell tower triangulation or via wifi networks.
Android contains the android.location package which provides the API to determine the
current geo position.
2.2. LocationManager
The LocationManager class provides access to the Android location service. This services
allows to access location providers, to register location update listeners and proximity alerts and
more.
2.3. LocationProvider
The LocationProvider class is the superclass of the different location providers which deliver
the information about the current location. This information is stored in the Location class.
The Android device might have several LocationProvider available and you can select which
one you want to use. In most cases you have the followng LocationProvider available.
Table 1. LocationProvider
LocationProvider Description
Uses the mobile network or WI-Fi to determine the best location. Might have
network
a higher precision in closed rooms then GPS.
gps
Use the GPS receiver in the Android device to determine the best location
via satellites. Usually better precision then network.
passive
Allows to participate in location of updates of other components to save
energy
2.4. Selecting LocationProvider via Criteria
For a flexible selection of the best location provider use a Criteria object, in which you can
define how the provider should be selected.
You can register a LocationListener object with the LocationManager class to receive
periodic updates about the geoposition.
2.5. Proximity Alert
You can also register an Intent which allows to define a proximity alert, this alert will be
triggered if the device enters a area given by a longitude, latitude and radius (proximity alert).
2.6. Forward and reverse Geocoding
The Geocoder class allows to determine the geo-coordinates (longitude, laditude) for a given
address and possible addresses for given geo-coordinates.
This process is known as forward and reverse geocoding.
2.7. Security
If you want to access the GPS sensor, you need the ACCESS_FINE_LOCATION permission.
Otherwise you need the ACCESS_COARSE_LOCATION permission.
2.8. Prompt the user to Enabled GPS
The user can decide if the GPS is enabled or now.
You can find out, if a LocationManager is enabled via the isProviderEnabled() method. If its
not enabled you can send the user to the settings via an Intent with the
Settings.ACTION_LOCATION_SOURCE_SETTINGS action for the android.provider.Settings
class.
LocationManager service = (LocationManager)
getSystemService(LOCATION_SERVICE);
boolean enabled = service
.isProviderEnabled(LocationManager.GPS_PROVIDER);
//
//
//
if
Check if enabled and if not send user to the GSP settings
Better solution would be to display a dialog and suggesting to
go to the settings
(!enabled) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
Typically you would open an AlarmDialog prompt the user and if he wants to enable GPS or if
the application should be canceled.
You cannot enable the GPS directly in your code, the user has to do this.
3. Using GPS and setting the current location
3.1. Activating GPS on the emulator
You need to activate GPS on your test device. If you test on the emulator and its not activated
you "null" if you try to use a LocationManager.
The Google Map Activity should automatically activate the GPS device in the emulator but if
you want to use the location manager directly you need to do this yourself. Currently their seems
to be an issue with this.
Start Google Maps on the emulator and request the current geo-position, this will allow you to
activate the GPS. Send new GPS coordinates to the Android emulator.
3.2. Setting the geoposition
You can use the "DDMS" Perspective of Eclipse to send your geoposition to the emulator or a
connected device. For open this Perspective select Window → Open Perspective → Other →
DDMS.
In the Emulator Control part you can enter the geocoordinates and press "Send."
You can also set the geoposition the Android emulator via telnet. Open a console and connect to
your device. The port number of your device can be seen in the title area of your emulator.
telnet localhost 5554
Set the position via the following command.
geo fix 13.24 52.31
4. Tutorial: Using the Android Location API
4.1. Create Project
Create a new project called "de.vogella.android.locationapi.simple" with the Activity called
"ShowLocationActivity".
This example will not use the Google Map therefore, it also runs on an Android device.
Change your "main.xml" layout file from the "res/layout" folder to the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dip"
android:orientation="horizontal" >
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="5dip"
android:text="Latitude: "
android:textSize="20dip" >
</TextView>
<TextView
android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="unknown"
android:textSize="20dip" >
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/TextView03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="5dip"
android:text="Longitute: "
android:textSize="20dip" >
</TextView>
<TextView
android:id="@+id/TextView04"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="unknown"
android:textSize="20dip" >
</TextView>
</LinearLayout>
</LinearLayout>
4.2. Add permissions
Add the following permissions to your application in your "AndroidManifest.xml" file



INTERNET
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
4.3. Activity
Change ShowLocationActivity to the following. It queries the location manager and display
the queried values in the activity.
package de.vogella.android.locationsapi.simple;
import
import
import
import
import
import
import
import
import
android.app.Activity;
android.content.Context;
android.location.Criteria;
android.location.Location;
android.location.LocationListener;
android.location.LocationManager;
android.os.Bundle;
android.widget.TextView;
android.widget.Toast;
public class ShowLocationActivity extends Activity implements LocationListener {
private TextView latituteField;
private TextView longitudeField;
private LocationManager locationManager;
private String provider;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
latituteField = (TextView) findViewById(R.id.TextView02);
longitudeField = (TextView) findViewById(R.id.TextView04);
// Get the location manager
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
// Define the criteria how to select the locatioin provider -> use
// default
Criteria criteria = new Criteria();
provider = locationManager.getBestProvider(criteria, false);
Location location = locationManager.getLastKnownLocation(provider);
// Initialize the location fields
if (location != null) {
System.out.println("Provider " + provider + " has been selected.");
int lat = (int) (location.getLatitude());
int lng = (int) (location.getLongitude());
latituteField.setText(String.valueOf(lat));
longitudeField.setText(String.valueOf(lng));
} else {
latituteField.setText("Provider not available");
longitudeField.setText("Provider not available");
}
}
/* Request updates at startup */
@Override
protected void onResume() {
super.onResume();
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
/* Remove the locationlistener updates when Activity is paused */
@Override
protected void onPause() {
super.onPause();
locationManager.removeUpdates(this);
}
@Override
public void onLocationChanged(Location location) {
int lat = (int) (location.getLatitude());
int lng = (int) (location.getLongitude());
latituteField.setText(String.valueOf(lat));
longitudeField.setText(String.valueOf(lng));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
Toast.makeText(this, "Enabled new provider " + provider,
Toast.LENGTH_SHORT).show();
}
@Override
public void onProviderDisabled(String provider) {
Toast.makeText(this, "Disabled provider " + provider,
Toast.LENGTH_SHORT).show();
}
}
4.4. Run and Test
If you using the emulator send some geo-coordinates to your device. These geo-coordinate
should be displayed as soon as you press the button.
5. Google Maps
5.1. MapsView
Google provides via the com.google.android.maps package a library for using the MapView
class in your application. This view allows to embed Google Maps into your application.
You require an additional key to use them. This key will be specified in the View which displays
the map.
You need to add the to your AndroidManifest.xml file in the information that you will be used
the MapView. The Android project creation wizard in Eclipse does this automatically, if you
select a Google API version.
<uses-library android:required="true"
android:name="com.google.android.maps"></uses-library>
The usage of MapView requires the permission to access the Internet, as the data displayed in the
MapView is downloaded from the Internet.
5.2. MapsActivity
The MapActivity class extends the Activity class and provides the life-cycle management and
the services for displaying a MapView widget.
MapActivity simplify the handling MapViews similar to ListActivity simplifies the usage of
ListViews.
A MapView is typically defined in the XML layout file used by the MapActivity and requires the
API key in the "android:apiKey" attribute. A MapView can be used with other user interface
components in the same layout.
The MapController class can be used to interact with the MapView, e.g. by moving it. A
Geopoint is a position described via latitude and longitude.
5.3. MyLocationOverlay
The MyLocationOverlay class allow to display the current geolocation and allows to enable a
compass.
The following code snippets shows how you can register myLocationOverlay for your MapView.
// This goes into the onCreate method
myLocationOverlay = new MyLocationOverlay(this, mapView);
mapView.getOverlays().add(myLocationOverlay);
myLocationOverlay.runOnFirstFix(new Runnable() {
public void run() {
mapView.getController().animateTo(
myLocationOverlay.getMyLocation());
}
});
You also have a enable and disable myLocationOverview and the compass in the onResume()
method and in the onPause() method.
// in onResume
overlay.enableCompass();
overlay.enableMyLocation();
// in onPause
overlay.disableCompass();
overlay.disableMyLocation();
5.4. ItemizedOverlay and OverlayItems
You can put instances of the Overlay class on the map. Overlay is the base class representing an
overlay which may be displayed on top of a map. To add an overlay, subclass this class, create
an instance, and add it to the list obtained from MapView.getOverlays().
ItemizedOverlay
ItemizedOverlay
is the base class for an Overlay which consists of a list of OverlayItems.
handles sorting north-to-south for drawing, creating span bounds, drawing a
marker for each point, and maintaining a focused item. It also matches screen-taps to items, and
dispatches Focus-change events to an optional listener.
5.5. Getting the Google Map key
To use Google Maps you need to create a valid key. This is based on the key with which you
sign your Android application during deployment. If you develop with Eclipse, Eclipse
automatically creates and uses a debug key based on the "userhome"/.android/debug.keystore
file.
To create the key you use the "keytool" command from your JDK installation pointing to the
"debug.keystore" file.
keytool -list -alias androiddebugkey -keystore
<path_to_debug_keystore>debug.keystore -storepass android -keypass android
The output of this command must be copied and entered on the following website: Google Maps
Signup Page .
The procedure is described in detail in the following link: Getting a Google Maps key .
5.6. Device with Google API
In case you want to use Google Maps in your emulator you have to create a device which
supports the Google API's. This requires that you also install the "Google API". During device
creation select the target Google API's in the version of your SDK.
6. Tutorial: Google Maps
6.1. Create Project
In the following chapter we will build an Android application which shows a MapView. It also
shows a compass and the current geoposition. Via an ItemizedOverlay implementation we will
also display the last 5 positions on the map.
Create a new Android project called "de.vogella.android.locationapi.maps" with an Activity
called "ShowMapActivity". Make sure to select the "Google API" als Target.
Add the following permissions to your application.



INTERNET
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
6.2. Validate AndroidManifest.xml
Validate that your "AndroidManifest.xml" file looks similiar to the following.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.vogella.android.locationapi.maps"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<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:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<uses-library android:name="com.google.android.maps" />
<activity
android:name="ShowMapActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
6.3. ItemizedOverlay
Create the following MyOverlays class which extends ItemizedOverlay class.
package de.vogella.android.locationapi.maps;
import
import
import
import
import
import
android.app.AlertDialog;
android.app.AlertDialog.Builder;
android.content.Context;
android.content.DialogInterface;
android.graphics.drawable.Drawable;
android.widget.Toast;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
public class MyOverlays extends ItemizedOverlay<OverlayItem> {
private
private
private
private
private
static int maxNum = 5;
OverlayItem overlays[] = new OverlayItem[maxNum];
int index = 0;
boolean full = false;
Context context;
private OverlayItem previousoverlay;
public MyOverlays(Context context, Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
this.context = context;
}
@Override
protected OverlayItem createItem(int i) {
return overlays[i];
}
@Override
public int size() {
if (full) {
return overlays.length;
} else {
return index;
}
}
public void addOverlay(OverlayItem overlay) {
if (previousoverlay != null) {
if (index < maxNum) {
overlays[index] = previousoverlay;
} else {
index = 0;
full = true;
overlays[index] = previousoverlay;
}
index++;
populate();
}
this.previousoverlay = overlay;
}
protected boolean onTap(int index) {
OverlayItem overlayItem = overlays[index];
Builder builder = new AlertDialog.Builder(context);
builder.setMessage("This will end the activity");
builder.setCancelable(true);
builder.setPositiveButton("I agree", new OkOnClickListener());
builder.setNegativeButton("No, no", new CancelOnClickListener());
AlertDialog dialog = builder.create();
dialog.show();
return true;
};
private final class CancelOnClickListener implements
DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "You clicked yes", Toast.LENGTH_LONG)
.show();
}
}
private final class OkOnClickListener implements
DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "You clicked no", Toast.LENGTH_LONG).show();
}
}
}
We will later use an image called "point". Put one icon called "point", e.g. "point.png" in at least
on of your drawable folder. If you don't know where to find icons, you can copy the icons
created by the Android wizard.
6.4. Layout
Create a MapView key as described earlier.
Change the "main.xml" layout file in your "res/layout" folder to the following.
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="Your Maps API Key"
android:clickable="true" />
and Replace "Your Maps API Key" with your Google Maps API key.
6.5. Activity
Change your Activity to the following. This Activity use an LocationListner to update the
MapView with the current location.
package de.vogella.android.locationapi.maps;
import
import
import
import
import
import
android.content.Context;
android.graphics.drawable.Drawable;
android.location.Location;
android.location.LocationListener;
android.location.LocationManager;
android.os.Bundle;
import
import
import
import
import
import
com.google.android.maps.GeoPoint;
com.google.android.maps.MapActivity;
com.google.android.maps.MapController;
com.google.android.maps.MapView;
com.google.android.maps.MyLocationOverlay;
com.google.android.maps.OverlayItem;
public class ShowMapActivity extends MapActivity {
private
private
private
private
private
MapController mapController;
MapView mapView;
LocationManager locationManager;
MyOverlays itemizedoverlay;
MyLocationOverlay myLocationOverlay;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main); // bind the layout to the activity
// Configure the Map
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapView.setSatellite(true);
mapController = mapView.getController();
mapController.setZoom(14); // Zoon 1 is world view
locationManager = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,
0, new GeoUpdateHandler());
myLocationOverlay = new MyLocationOverlay(this, mapView);
mapView.getOverlays().add(myLocationOverlay);
myLocationOverlay.runOnFirstFix(new Runnable() {
public void run() {
mapView.getController().animateTo(
myLocationOverlay.getMyLocation());
}
});
Drawable drawable = this.getResources().getDrawable(R.drawable.point);
itemizedoverlay = new MyOverlays(this, drawable);
createMarker();
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
public class GeoUpdateHandler implements LocationListener {
@Override
public void onLocationChanged(Location location) {
int lat = (int) (location.getLatitude() * 1E6);
int lng = (int) (location.getLongitude() * 1E6);
GeoPoint point = new GeoPoint(lat, lng);
createMarker();
mapController.animateTo(point); // mapController.setCenter(point);
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}
private void createMarker() {
GeoPoint p = mapView.getMapCenter();
OverlayItem overlayitem = new OverlayItem(p, "", "");
itemizedoverlay.addOverlay(overlayitem);
if (itemizedoverlay.size() > 0) {
mapView.getOverlays().add(itemizedoverlay);
}
}
@Override
protected void onResume() {
super.onResume();
myLocationOverlay.enableMyLocation();
myLocationOverlay.enableCompass();
}
@Override
protected void onPause() {
super.onResume();
myLocationOverlay.disableMyLocation();
myLocationOverlay.disableCompass();
}
}
6.6. Run and Test
Run and test your application. You should be able to zoom in and out and send new geo
coordinates to your Activity via the Emulator.
7. Thank you
Please help me to support this article:
8. Questions and Discussion
Before posting questions, please see the vogella FAQ. If you have questions or find an error in
this article please use the www.vogella.com Google Group. I have created a short list how to
create good questions which might also help you.
9. Links and Literature
9.1. Source Code
Source Code of Examples
9.2. Android Resources
Introduction to Android Development
Android Location API and Google Maps
Android Homepage
9.3. vogella Resources
Eclipse RCP Training (German) Eclipse RCP Training with Lars Vogel
Android Tutorial Introduction to Android Programming
GWT Tutorial Program in Java and compile to JavaScript and HTML
Eclipse RCP Tutorial Create native applications in Java
JUnit Tutorial Test your application
Git Tutorial Put everything you have under distributed version control system
Download