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
The following assumes that you have already basic knowledge in Android development. Please check theAndroid development tutorial for the basics.
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 contains the
android.location
package which provides the API to determine the current geo position.
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.
The
The Android device might have several
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 |
---|---|
network | Uses the mobile network or WI-Fi to determine the best location. Might have 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 |
For a flexible selection of the best location provider use a
You can register 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.
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).
The
This process is known as forward and reverse geocoding.
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.
If you want to access the GPS sensor, you need the
ACCESS_FINE_LOCATION
permission. Otherwise you need the ACCESS_COARSE_LOCATION
permission.
The user can decide if the GPS is enabled or now.
You can find out, if a LocationManager is enabled via the
Typically you would open an
You cannot enable the GPS directly in your code, the user has to do this.
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 theSettings.ACTION_LOCATION_SOURCE_SETTINGS
action for the android.provider.Settings
class.
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean enabled = service
.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 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
if (!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.
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
The Google Map
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.
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.
You can use the "DDMS"
In the Emulator Control part you can enter the geocoordinates and press "Send."
You can als 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.
Set the position via the following command.
Perspective
of Eclipse to send your geoposition to the emulator or a connected device. For open this Perspective
select → → → .In the Emulator Control part you can enter the geocoordinates and press "Send."
You can als 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
Create a new project called "de.vogella.android.locationapi.simple" with the Activity calledShowLocationActivity.
This example will not use the Google Map therefore, it also runs on an Android device.
Change your
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:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
"@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dip"
android:orientation="horizontal" >
"@+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" >
"@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="unknown"
android:textSize="20dip" >
"@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
"@+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" >
"@+id/TextView04"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="unknown"
android:textSize="20dip" >
Add the following permissions to your application in your
AndroidManifest.xml
file- INTERNET
- ACCESS_FINE_LOCATION
- ACCESS_COARSE_LOCATION
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 android.app.Activity; import android.content.Context; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.widget.TextView; import 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(); } }
Google provides via the
You require an additional key to use them. This key will be specified in the
You need to add the to your AndroidManifest.xml file in the information that you will be used the
The usage of
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."true" android:name="com.google.android.maps">
The usage of
MapView
requires the permission to access the Internet, as the data displayed in theMapView
is downloaded from the Internet.
The
MapActivity simplify the handling
A
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 ofListViews
.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.MapView
allows to activate the build in zoom controls via the .setBuiltInZoomControls()
method call.
The
The following code snippet shows how you can register myLocationOverlay for your
You also have a enable and disable myLocationOverview and the compass in the
MyLocationOverlay
class allow to display the current geolocation and allows to enable a compass.The following code snippet 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();
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
is the base class for an Overlay
which consists of a list of OverlayItems
.ItemizedOverlay
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.
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.
If you are using Java7 the default output of keytool has changed. Add the
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 .
To create the key you use the "keytool" command from your JDK installation pointing to the "debug.keystore" file.
keytool -list -alias androiddebugkey \
-keystore debug.keystore \
-storepass android -keypass android
If you are using Java7 the default output of keytool has changed. Add the
-v
to see also the MD5 fingerprint.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 .
In the following chapter we will build an Android application which shows a
Create a new Android project called "de.vogella.android.locationapi.maps" with an
Add the following permissions to your application.
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
calledShowMapActivity. Make sure to select the "Google API" als Target.Add the following permissions to your application.
- INTERNET
- ACCESS_FINE_LOCATION
- ACCESS_COARSE_LOCATION
Validate that your
AndroidManifest.xml
file looks similiar to the following file.
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
package="de.vogella.android.locationapi.maps"
android:versionCode="1"
android:versionName="1.0" >
"15" />
"android.permission.INTERNET" />
"android.permission.ACCESS_FINE_LOCATION" />
"android.permission.ACCESS_COARSE_LOCATION" />
"@drawable/ic_launcher"
android:label="@string/app_name" >
"com.google.android.maps" />
"ShowMapActivity"
android:label="@string/app_name" >
"android.intent.action.MAIN" />
"android.intent.category.LAUNCHER" />
Create the following
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 do not know where to find icons, you can copy the icons created by the Android wizard.
MyOverlays
class which extends ItemizedOverlay
class.
package de.vogella.android.locationapi.maps;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.widget.Toast;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
public class MyOverlays extends ItemizedOverlay {
private static int maxNum = 5;
private OverlayItem overlays[] = new OverlayItem[maxNum];
private int index = 0;
private boolean full = false;
private 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 do not know where to find icons, you can copy the icons created by the Android wizard.
Create a
Change the
Replace
MapView
key as described earlier.Change the
main.xml
layout file in your res/layout
folder to the following."1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android" android:id="@+id/mapview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:apiKey="your_key" android:clickable="true" />
Replace
your_key
with your Google Maps API key.
Change your
Activity
to the following. This Activity
use an LocationListner
to update theMapView
with the current location.
package de.vogella.android.locationapi.maps;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.OverlayItem;
public class ShowMapActivity extends MapActivity {
private MapController mapController;
private MapView mapView;
private LocationManager locationManager;
private MyOverlays itemizedoverlay;
private 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();
}
}
Không có nhận xét nào:
Đăng nhận xét