First Android Application Released – FillLPG

To those of you who are are following the mini-series on using maps in Android it will not come as a great surpise that I have now released my first Android application which implements Google Maps.

The free application is called FillLPG and can be downloaded from the Android Market.

The application is aimed at drivers in the UK who have vehicles running on LPG (Liquefied Petroleum Gas) and want to be able to locate the cheapest filling station with the data coming from an existing website (FillLPG) where registered users can add new stations and update prices of existing ones so that the data remains accurate.

I used the development mainly as an exercise in learning Android using a real world application rather than the contrived examples you find in books and on the web. On the whole it has been a very useful exercise and together with the FillLPG website owner I intend to add functionality to the app over the coming months.

After the first day the application has been installed 20 times and only uninstalled once – not a bad ratio in my opinion, especially when I suspect that the person who uninstalled the app was probably the same one who complained that there were stations missing and that the prices were out of date.

I have to say that I put my head in my hands – the whole point of the website is that the LPG community at large maintains the data for the good of the community. The application is an extension of this to allow users to update prices on the go and to locate stations in their area and across the country – really useful when travelling to a new area.

If you download the application and have any problems, feedback (good or bad – just please be constructive) or suggestions then please feel free to leave a comment on this posting.

Using Google Maps in your Android Applications – Part 4 [Displaying Information Popups]

This tutorial was created using the Google Maps API v1 and as it is not possible to obtain a v1 key anymore this series of posts is pretty much obsolete. I’ve been looking at creating an updated set of tutorials and I’ll post them as soon as they are ready.

A little later than hoped and by popular demand (OK, at the time of writing there were only two requests in the comments of the previous part) this is the last part of this mini-series looking at using Google Maps in your Android applications.

In previous parts we have configured our project and displayed a simple map, centered the map on our current location and displayed markers on points of interest. Now we are going to allow the user to tap on one of these markers in order to see additional details about the associated location.

If you’ve not been following along with the series then you can download the working source code from here – note that you will need to insert your own Google Maps API key into the main.xml view file.

Assuming that you are all ready to go we’ll jump straight in.

The approach I’m going to use is possible because the MapView class inherits from ViewGroup – i.e. a group of view no less. So all we need to do is to create a view containing our data and display it at the appropriate location on top of the map.

The first thing we are going to need is a suitable image which will form the background to our view. As I’m a developer and not a designer I did a search on OpenClip art and found a suitable speech bubble for this project.

Now we’re not just going to use the image as is, remember that Android supports numerous screen sizes and we need to make sure that they will scale nicely. To that end we are going to make it into a 9-patch image which will allow it to grow/shrink in a manner that will not cause it to look all nasty and stretched. There are a couple of great articles on what a 9-patch image is and how to use the draw9patch utility within the SDK to create one so I’ll not regurgitate that here. Suffice to say that the utility allow you to open up the image and define which areas should stretch and which areas should not.

With our 9-patch image created, I’ve named mine bubble.9.png, we need to copy it into the res/drawable folder within our project – create the folder if it is not there already.

Before you head down to the comment section to berate me for not using the density independant folders remember that this is a tutorial for maps, not images 😉

Your project should now look like this.

With the image in place we can create our new view. Expand the res/layout folder in the project and add a new file called bubble.xml. When the file opens, switch to the xml view (using the tabs at the bottom of the window) and enter the following xml then save the file.

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/infoBubble"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/bubble"
    android:orientation="vertical"
    android:baselineAligned="false" >
     
<TextView
    android:id="@+id/locationName"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="#"
    android:textSize="16dip"
    android:textColor="@android:color/black" />
     
</LinearLayout>

What we’ve done here is to create a simple view using a LinearLayout (where each element is placed after the previous one) and a TextView which will display our content. if you click on the Graphical Layout tab in the designer you will see something that looks like this:

Now is a good time to review what we actually want to achieve here

  • If the user taps a marker on the screen then we need to:
    • Close any open popups
    • Fetch data for tapped location
    • Configure the new popup
    • Display the popup in the appropriate location
    • Center the map on the marker location
  • If the user taps the screen but does not hit a marker then we need to:
    • Close any open popups

There are a couple of minor tweaks we need to make before we can get started on coding the new functionality. First of all we need to maintain a list of the displayed markers – we only have one in this example but I’m assuming that your application will have a few more 😉 Add the following class variables.

private ArrayList _displayedMarkers; 
private LinearLayout _bubbleLayout; 

and update the if block on the draw method as follows:

@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
 
    super.draw(canvas, mapView, shadow);
 
    Projection projection = mapView.getProjection();
 
    int latSpan = mapView.getLatitudeSpan();
    int lngSpan = mapView.getLongitudeSpan();
    GeoPoint mapCenter = mapView.getMapCenter();
    int mapLeftGeo = mapCenter.getLongitudeE6() - (lngSpan / 2);
    int mapRightGeo = mapCenter.getLongitudeE6() + (lngSpan / 2);
 
    int mapTopGeo = mapCenter.getLatitudeE6() - (latSpan / 2);
    int mapBottomGeo = mapCenter.getLatitudeE6() + (latSpan / 2);
 
    _displayedMarkers = new ArrayList<GeoPoint>();
 
    GeoPoint geoPoint = this.getSampleLocation();
 
    if ((geoPoint.getLatitudeE6() > mapTopGeo && geoPoint.getLatitudeE6() < mapBottomGeo)
            && (geoPoint.getLongitudeE6() > mapLeftGeo && geoPoint.getLongitudeE6() < mapRightGeo)) {
 
        Point myPoint = new Point();
        projection.toPixels(geoPoint, myPoint);
 
        Bitmap marker = BitmapFactory.decodeResource(mapView.getContext().getResources(), R.drawable.markerblue);
 
        canvas.drawBitmap(marker, myPoint.x - 15, myPoint.y - 30, null);
 
        _displayedMarkers.add(geoPoint); // Add this line ....
    }
}

With the list of displayed markers being maintained we can now iterate over them to see if the user has tapped the map somewhere close to one of them. The entry point for our functionality is the onTap event of our overlay so open this file and add the following.

@Override
public boolean onTap(GeoPoint p, MapView mapView) { 
    // If a bubble is currently displayed then clear it. 
    if(_bubbleLayout != null){ 
        mapView.removeView(_bubbleLayout); 
    } 
    if (performHitTest(mapView, p)) { 
        // Get instance of the Bubble Layout 
        LayoutInflater inflater = (LayoutInflater) mapView.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
        LinearLayout bubbleLayout = (LinearLayout) inflater.inflate(R.layout.bubble, mapView, false); 
    } 
    return true; 
} 

What we are doing here is to close any previous bubbles and then call out to the performHitTest method which we’ll write now. The aim of this method is to compare the location that the user tapped with the currently displayed marker positions. If they are in close proximity then we’ll create a new instance of the bubble view, configure it and display it.

As with all things there are numerous ways to determine whether the user has tapped the screen close enough to a marker to consider it to be a ‘hit’. For the purpose of this example I’m going to ‘draw’ an invisible box around the tapped point and then iterate through the displayed markers to see if their position falls within that area. If it does then it’s a hit, if not then it’s a miss. Obviously this method has some shortcomings, especially when multiple markers fall inside the hit box, but you’ll get the idea.

A basic implementation of this would be as follows:

@Override
public boolean onTap(GeoPoint p, MapView mapView) {
 
    // If a bubble is currently displayed then clear it..
    if (_bubbleLayout != null) {
        mapView.removeView(_bubbleLayout);
    }
 
    if (performHitTest(mapView, p)) {
 
        // Get instance of the Bubble Layout ...
        LayoutInflater inflater = (LayoutInflater) mapView.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        _bubbleLayout = (LinearLayout) inflater.inflate(R.layout.bubble, mapView, false);
 
        // .. configure its layout parameters
        MapView.LayoutParams params = new MapView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, p,
                MapView.LayoutParams.BOTTOM_CENTER);
 
        _bubbleLayout.setLayoutParams(params);
 
        // Locate the TextView
        TextView locationNameText = (TextView) _bubbleLayout.findViewById(R.id.locationName);
         
        // Set the Text
        locationNameText.setText(getSampleText());
 
        // Add the view to the Map
        mapView.addView(_bubbleLayout);
         
        // Animate the map to center on the location
        mapView.getController().animateTo(p);
    }
    return true;
};

If you run the application now you should see the emulator spin up and display our map with the single location marker in place. Click on the marker and you should (hopefully) see something like this:

While this is a working implementation of the application there are some obvious issues here.

  • We have a single, hardcoded location whereas in the real world you will more than likely want to display locations which are stored in a database or accessed via a webservice (or both).
    • This can be achieved by updating the draw event in the overlay to fetch the locations and then loop through them, adding those which are visible in the current view of the map.
    • We are using basic a GeoPoint for our location but your application would probably require a custom class with latitude/longitude properties. The current performHitTest method only returns true or false if a location is close to the tapped location.
      • The performHitTest method would need to be updated to return or set the ‘hit location’ object which will probably contain additional information to be displayed in the bubble.

This is the last part of this mini-series which I hope you have found useful. Please feel free to leave any feedback in the comments and remember that you can download the fully working Eclipse project from here.

Happy mapping 🙂

Using Google Maps in your Android Applications – Part 3 [Adding Place Markers]

This tutorial was created using the Google Maps API v1 and as it is not possible to obtain a v1 key anymore this series of posts is pretty much obsolete. I’ve been looking at creating an updated set of tutorials and I’ll post them as soon as they are ready.

If you have been following along with Part 1 and Part 2 of this mini-series you should now have a basic Android application which displays your current location on a map. Not amazing functionality I’ll admit but the basis for many location based applications in the Android Market. What will make the application truly useful is the ability to add markers at specific locations, e.g. points of interest, cash machines, checkpoints etc.

While this installment is about how to display the markers at the appropriate map locations it will not cover how that location data is obtained. In some cases it may be possible, although unlikely, to hard code the locations into the application itself. Alternatively the data may be provided via a webservice of some description and that data may or may not be persisted in the built-in SQLite database (as it is in my application). For the purpose of this exercise we’ll be hard coding the data in the application.

When I started my application this was the part I found the most frustrating, mainly because there was so little information out there and what there was seemed over complicated, poorly presented and (as it turned out) not very efficient for my requirements, i.e. the map took far too long to render. The reason for this was that most of the blog posts were advising that using ItemizedOverlay was the way to go. I duly followed along and had my application working – but it was too slow to be usable. Adding some debug output I could see that it was taking about half a second to plot each point and I had over 600 of these so I’m sure you can do the maths on this and see my problem.

Now it could well be that I had an issue with my code (although I had followed the process described in numerous blog posts) and before everyone starts adding comments that I must have been calling populate() too often I can assure you i was not. I have subsequently read that ItemizedOverlay is only really suitable for ‘a dozen or so’ items and that performance degrades significantly as the number of items increases – that was certainly my experience.

The solution I’ve adopted is to create an overlay class which extends Overlay instead of ItemizedOverlay and as ItemizedOverlay itself also extends Overlay all I’m doing here is cutting out the middleman. As we start to add code to this class it will be necessary to import a number of namespaces and for the sake of brevity I will assume that you are comfortable with doing this. If not then don’t panic, the final class code will be provided within the article and of course within the project download for this installment.

With the preamble out of the way, lets get coding. If you have been following along then you can use the project you have created by following along with this mini-series, or you can download the project from here.

With the project open in Eclipse

  • Right-click on the main package file (within the src folder and in my case called com.onthefencedevelopment.androidmaps101)
  • Click New followed byClass in the resulting menus.

In the resulting ‘Java Class’ dialog specify a suitable name for the new class, I’ve used DemoOverlay, and replace the default Superclass value with com.google.android.maps.Overlay.

Eclipse will now create a template class ready for our code. Position the cursor between the curly braces and press CTRL+SPACE. This will bring up a list of all of the methods of the Overlay class that can be overridden. Scroll down to the draw method which takes three parameters (context, mapview and shadow) and press enter – Eclipse will now create a method stub with the appropriate signature.

Your Overlay class should now look like this:


package com.onthefencedevelopment.androidmaps101;
import android.graphics.Canvas;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
public class DemoOverlay extends Overlay {
    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
        // TODO Auto-generated method stub
        super.draw(canvas, mapView, shadow);
    }
}

Our code is going to start after the call to the superclass and the first thing we are going to do is to obtain a reference to the MapViews Projection object. The Projection object translates between on-screen pixels and latitude/longitude pairs and is obtained with a single line of code:


Projection projection = mapView.getProjection();

Next we need to determine the latitude/longitude range of our screen, i.e. where in the world we are looking and how much of it we can see.

int latSpan = mapView.getLatitudeSpan();
int lngSpan = mapView.getLongitudeSpan();
GeoPoint mapCenter = mapView.getMapCenter();
int mapLeftGeo = mapCenter.getLongitudeE6() - (lngSpan / 2);
int mapRightGeo = mapCenter.getLongitudeE6() + (lngSpan / 2);
int mapTopGeo = mapCenter.getLatitudeE6() - (latSpan / 2);
int mapBottomGeo = mapCenter.getLatitudeE6() + (latSpan / 2);

The first three lines call on the MapView methods to obtain the central latitude/longitude and the screen width and height in terms of longitude and latitude respectively. The next four lines calculate the coordinates of the edges of the screen in relation to the map.

The reason we have done this is so that we can determine whether or not to add a particular marker to the overlay or not – there is no point adding it if it will never be seen and it will only waste resources if we do.

As already mentioned, I’m not going to cover how the location data is obtained, stored or retrieved so I’m going to hard code it for the sake of this exercise. So, add the following method which will return a single GeoPoint for an as yet unknown location 😉

rivate GeoPoint getSampleLocation() {
    // Create GeoPoint to secret location....
    GeoPoint sampleGeoPoint = 
        new GeoPoint((int)(56.27058500725475 * 1E6),
            (int)(-2.6984095573425293 * 1E6));
    return sampleGeoPoint;
}

Now we can declare and instantiate a GeoPoint variable by calling the method from within our draw method. Add this just after the calculations for the screen boundaries.


GeoPoint geoPoint = this.getSampleLocation();

Now that we have our GeoPoint we need to determine if it is visible within the part of the map displayed on the screen. This is a simple matter of comparing it with the values we calculated earlier:


if ((geoPoint.getLatitudeE6() > mapTopGeo && geoPoint.getLatitudeE6() < mapBottomGeo)
        && (geoPoint.getLongitudeE6() > mapLeftGeo && geoPoint.getLongitudeE6() < mapRightGeo)) {
 
    Point myPoint = new Point();
    projection.toPixels(geoPoint, myPoint);
    Bitmap marker = BitmapFactory.decodeResource(mapView.getContext().getResources(), R.drawable.markerblue);
    canvas.drawBitmap(stationMarker, myPoint.x - 15, myPoint.y - 30, null);
 
}

At this point Eclipse will complain about the R.drawable.markerblue argument we used when declaring and instantiating the marker bitmap. This is because it does not exist in the project yet. You can download it from here and save it to a folder called drawable within the res folder of the project (if the drawable folder is not present in the folder structure then just create it). You may have to refresh the project afterward by right clicking on it in the Package Explorer and clicking Refresh.

The completed overlay code should look like this:

package com.onthefencedevelopment.androidmaps101;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
 
public class DemoOverlay extends Overlay {
 
    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
        super.draw(canvas, mapView, shadow);
 
        Projection projection = mapView.getProjection();
 
        int latSpan = mapView.getLatitudeSpan();
        int lngSpan = mapView.getLongitudeSpan();
        GeoPoint mapCenter = mapView.getMapCenter();
        int mapLeftGeo = mapCenter.getLongitudeE6() - (lngSpan / 2);
        int mapRightGeo = mapCenter.getLongitudeE6() + (lngSpan / 2);
 
        int mapTopGeo = mapCenter.getLatitudeE6() - (latSpan / 2);
        int mapBottomGeo = mapCenter.getLatitudeE6() + (latSpan / 2);
 
        GeoPoint geoPoint = this.getSampleLocation();
 
        if ((geoPoint.getLatitudeE6() > mapTopGeo && geoPoint.getLatitudeE6() < mapBottomGeo)
         && (geoPoint.getLongitudeE6() > mapLeftGeo && geoPoint.getLongitudeE6() < mapRightGeo)) {
 
            Point myPoint = new Point();
            projection.toPixels(geoPoint, myPoint);
 
            Bitmap marker = BitmapFactory.decodeResource(mapView.getContext().getResources(), R.drawable.markerblue);
 
            canvas.drawBitmap(marker, myPoint.x - 15, myPoint.y - 30, null);
        }
    }
 
    private GeoPoint getSampleLocation() {
 
        // Create GeoPoint to secret location....
        GeoPoint sampleGeoPoint = new GeoPoint((int) (56.27058500725475 * 1E6), (int) (-2.6984095573425293 * 1E6));
 
        return sampleGeoPoint;
    }
}

We are done with this class now so close it and open the OpenMap.java file we created in part 1 and enhanced in part 2. Within the onCreate method add the following two lines at the end.

DemoOverlay demoOverlay = new DemoOverlay();
mapView.getOverlays().add(demoOverlay);

These simply create a new instance of the overlay and add it to the MapView overlays collection.

We can now run the application and see where in the world we have placed the marker.

If you pan and zoom into the marker location you will see that it is located in the East of Scotland at what appears to be quite an uninteresting location – but it is actually Scotlands Secret Bunker, which is of course not much of a secret anymore 😉

So what have we done here? Well, in just over 50 lines of code we’ve created an overlay class which will add a marker to a map surface using latitude/longitude coordinates. Ok the class is a bit contrived with a hard coded location but it can easily be extended to create markers for a collection of locations, bearing in mind that only those that fall within the viewable area of the map will be rendered.

Click here for the working project for this instalment, remember you’ll need to add your Google Maps API key.

If you have any problems with the project or notice something wrong with the code or the blog post then please leave a comment to let me know.

Where to next? Well, having locations on a map is one thing, but dropping a marker on a map is only part of the story – what we need to do next is allow a user to tap on a location and have additional information about it displayed on screen. That will be the subject of part 4 of this min-series and will probably hit the blog in the New Year.

Using Google Maps in your Android Applications – Part 2 [Showing Current Location]

This tutorial was created using the Google Maps API v1 and as it is not possible to obtain a v1 key anymore this series of posts is pretty much obsolete. I’ve been looking at creating an updated set of tutorials and I’ll post them as soon as they are ready.

In my last post I showed you how to create a basic Android application which would display a map and zoom in to a specific location, my home town. While the functionality was not what you could call awe-inspiring it does lay the foundations for most mapping applications and serves as a good starting point. You can download the code from Part 1 but you will need to insert your own Google Maps API key for it to display the maps.

In this installment I had intended to continue from where we left off last time and add the functionality required to display markers/pins on the map to indicate points of interest. There are however a couple of areas to cover and I didn’t want to end up writing a massive post to cover both – so I’m going to split this functionality into two parts; showing the current location and showing locations of points of interest.

To display the current location we are going to use the built-in ‘MyLocationOverlay’ component which (as the name suggests) displays your current location on the map and centers the view around it. The overlay itself only requires a handful of lines of code but there are a few things that you need to be aware of which we’ll cover later.

I’ll assume that you have the sample code from Part 1 (either you followed along last time or have just downloaded and imported it into Eclipse) and have your Google Maps API key inserted into the main view.

The first thing we are going to need to do is to request permission to access the Location sensors within the device. To do this we need to open the AndroidManifest.xml file as we did in Part 1 and click on the Permissions tab where you should see the INTERNET permission we added last time.

  1. Click on the ‘Add’ button and using the dropdown list select the ‘android.permission.ACCESS_FINE_LOCATION’ permission.
  2. Repeat the process to add ‘android.permission.ACCESS_COARSE_LOCATION’.

The difference between FINE and COURSE location is basically using either GPS or your Mobile Network Signal to determine your location. Your screen should now look something like this (note that while there are Move Up/Down buttons, for our purposes the order of these permissions is not important – or at least they do not seem to be).

Save and close the manifest – we’re done with that for now.

If it is not already open, locate the OpenMap.java file in the Package Explorer and double-click it to open it. The first thing we need to so is to remove the line of code that centers the map on Exmouth so locate the following line and remove it:

// Centre on Exmouth
mapController.setCenter(new GeoPoint((int) (50.634955 * 1E6),
                                     (int) (-3.409753 * 1E6)));

Next, after the declaration of the MapController we need to declare the MyLocationOverlay

private MapController mapController;
private MyLocationOverlay myLocation;  // Add this line

Eclipse will now display a red squiggle under MyLocationOverlay of the new line, this is because we have not told it where to find this type. By far the easiest way of rectifying this is to hover over the highlighted ‘word’ and wait for Eclipse to tell us what is wrong and, more importantly, how we can fix it. In this instance click on the ‘Import MyLocationOverlay’ option. A new line will be added under the existing imports and the squiggle will disappear.

Now we can update our onCreate method to create an instance of the overlay by adding the following code after the call to mapController.setBuiltInZoomControls:


// Add the MyLocationOverlay
myLocation = new MyLocationOverlay(this, mapView);
mapView.getOverlays().add(myLocation);
myLocation.enableMyLocation();

The first line instantiates the overlay, the second one adds it to the collection of overlays in the current MapView object and the last one tells it to start polling for location updates.

Many of the examples I read stated that this was all that was required to get the MyLocationOverlay working but nothing seemed to happen for me, running the application in the emulator gave me a map centered on Tulsa but refused to move even when I sent a location update to the emulator (more on that in a minute). After a lot of digging around I found that by adding the following snippet of code everything came together and started working.

myLocation.runOnFirstFix(new Runnable() {
    public void run() {
        mapController.animateTo(myLocation.getMyLocation());
    }
});

Basically this creates a thread which will run when the system has a location fix (it will run immediately if a fix has already been established). The code within the method simply tells the MapController to move to the specified location in much the same way as we centered on Exmouth but this time using dynamic values from the MyLocationOverlay.

Your onCreate method should now look like this:

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    // Get Mapping Controllers etc
    MapView mapView = (MapView) findViewById(R.id.map_view);
    mapController = mapView.getController();
    mapController.setZoom(11);
    mapView.setBuiltInZoomControls(true);
 
    // Add the MyLocationOverlay
    myLocation = new MyLocationOverlay(this, mapView);
    mapView.getOverlays().add(myLocation);
    myLocation.enableMyLocation();
 
    myLocation.runOnFirstFix(new Runnable() {
       public void run() {
           mapController.animateTo(myLocation.getMyLocation());
       }
    });
}

Ok, so we have a runnable application which should display the current location – but how do we test this without installing it on a location enabled device, i.e. how do we run it in the emulator and simulate location updates?

First we need to open a new perspective in Eclipse (a perspective is basically a preset arrangement of windows aimed at a particular purpose, e.g. Debug). From the Window menu select Open Perspective followed by Other from the resulting sub-menu. Select DDMS from the resulting dialog:

DDMS is the Dalvik Debug Monitor Server which is how we can not only see what is going on in the emulator but also interact with it. We will be using it to send location updates but the DDMS allows us to place fake calls and send fake SMS as well as providing access to the debug trace logs in real time.

Now we can start our application in the emulator and using the buttons in the top right corner of the code window we can open the DDMS perspective which should look something like this.

It looks a bit cluttered but I’m running a 15.6″laptop so your view may look less so and you can close/hide windows and move them around as you see for. Also note that I’ve scrolled the Emulator Control window down a bit to show the area we are interested in, the Location Controls.

In the Devices window you should see the running instance of the emulator(s) (or any connected Android devices in USB mode). If it is not already selected then do so now to tell the DDMS that we intend to interact with it.

In the Location Control window, enter a valid Latitude/Longitude pair and, with the Emulator in view if possible, press Send. Hopefully (unless you entered the coordinates for Tulsa) you will see the map move to display the location at the specified coordinates. [No prizes for guessing where those in the screenshot resolve to].

So that’s it we’re done, yes. Well, again, not quite. While this will work fine in the emulator in the real world and on real devices there is something we have not accounted for; What happens to our application when it’s running in the background, i.e. when the user has returned to the Home screen or opened another application? If you were to run this application on a real device with GPS enabled you would see that when you returned to the Home screen the GPS icon would still be displayed in the notification area. We have hooked ourselves onto the location sensors and if we leave them in this state we could degrade the performance of the device and even become a battery hog – users don’t like this one bit!

Fortunately the solution is pretty simple and with the addition of two override methods and a couple of lines of code we can be device friendly.

Add the following two methods to the OpenMap class, anywhere will do but I’ve put mine between the OnCreate and isRouteDisplayed methods;

@Override
protected void onResume() {
    super.onResume();
    myLocation.enableMyLocation();
}
 
@Override
protected void onPause() {
    super.onPause();
    myLocation.disableMyLocation();
}

OnPause and OnResume are called when the application is moved to the background and foreground respectively so here we are turning the location updates off and back on again as the application moves between these states.

I’ve installed this version of the application on my old HTC Hero and tested it in the car and everything worked as expected, the map even re-centered when the location marker reached the edge.

So there we are, with a few more lines of code we have a location aware map application which we will build on in Part 3 of this Mini-Series when we add a custom overlay which we can use to display our points of interest.

If you’ve had any problems following along (or if I’ve missed something when writing this up) then I’ve uploaded the Eclipse project for you to download here. Remember that you will need to add your Google Maps API key to the main.xml file (see Part 1 for more details).

Please feel free to leave a comment if you think I could improve this post – I’m not so proud that I can’t take it

Using Google Maps in your Android Applications – Part 1

This tutorial was created using the Google Maps API v1 and as it is not possible to obtain a v1 key anymore this series of posts is pretty much obsolete. I’ve been looking at creating an updated set of tutorials and I’ll post them as soon as they are ready.

I’m currently working on an Android application which displays points of interest on a map and allows the user to see additional details by tapping on the associated marker. This sounds like a straight forward requirement for an Android application and so you would have thought that there would be a wealth of resources on the web to guide a developer on his way – but that’s not been my experience.

While there are resources out there for the basics of how to get a map to display in your application, unless you really know what you are looking for it is quite difficult to find what you are looking for (at least that’s been my experience). Some blog posts include some code but I’ve not found any in the context of a full project – while it my be obvious to someone where the code should go it’s not always obvious to the reader.

This mini-series of posts will (hopefully) show you how to not only include a map but also how to display markers/pins at specific locations and display an information popup when the user taps on one of them. It’s the last part that really caused me some problems and now that I have a working solution I can pass it on.

In this first installment I’ll cover the basics, i.e. how to set up your solution and add code to display a simple map. I’ll also attach the sample, fully working, code which will be the starting point for Part 2 of the mini-series.

The first thing that you will need is a Google Maps API key – this will allow your application to download and display the map images (or tiles as they are known).

You’ll need a Google account to request a key but if you are working with Android then I presume this will not be an issue. To request your key go to http://code.google.com/android/maps-api-signup.html and follow the instructions. Store your key somewhere safe because as far as I can tell there is now way to view it without generating it again (which I find odd but there you go).

Armed with your API key you can now start to create your solution. I’m running Eclipse 3.6 (Helios) with the Android tools etc installed.

I’ve previously blogged about how I configured my system so if you need any assistance with this then take a look.

With Eclipse running, create your new project by selecting File > New > Project. In the resulting ‘Select a Wizard’ dialog, expand the Android node and select Android Application, then click Next. This will open up the ‘New Android Project’ dialog as below:

Here I have named the project ‘androidmaps101’, specified the project location as being the default workspace and selected my build target as being Google APIs version 2.2. It is important to use the Google APIs version otherwise the mapping functionality will not work. If you want to target a different overall version of Android, e.g. 2.1, then simply download and select the appropriate Google APIs version (note that I only have versions 2.1 and 2.2 installed on my system at the moment).

In the bottom half of the dialog I’ve specified the Application Name as AndroidMaps, provided a suitable package name and specified the minimum SDK version as 8 (to match that of the Google API build target I specified above). I’ve also specified that an activity of OpenMap be created as part of the project – we’ll see where all these settings end up in a minute.

At this point you can click Finish to kickoff the wizard (clicking Next will let you create a Test project but we’ll not need that for this mini-series).

Once the wizard has finished, open your Package Explorer and you’ll see the newly created project called ‘androidmaps101’. Expand this to display the package which can be expanded to display our initial activity – called ‘OpenMap’.

Before we dig into the code we need to make a few tweaks to the project settings, in particular we need to ensure that we include a reference to the Google maps library and add a permission setting to allow access to the Internet in order to download the maps.

In the root folder of the new project is a file called AndroidManifest.xmlwhich contains our project properties. Double-click to open this file and it will open in multi-tabbed interface as below:

  1. Click on the ‘Application‘ tab and scroll to the bottom of the screen until you see the Application Nodes section.
  2. Click the Add button and then select ‘Uses Library‘ in the resulting dialog and click OK – you should now see an new value ‘Uses Library‘ in the Application Nodes list.
  3. Make sure this is selected and then click on the ‘Name‘ drop-down list to the right of the screen.
  4. Select ‘com.google.android.maps‘ from this list and set the ‘Required‘ drop-down to true.

That’s the library sorted, now for the Internet permission.

  1. Select the ‘Permissions‘ tab and click the ‘Add‘ button.
  2. Select ‘Uses Permission‘ from the resulting dialog and click OK. With this selected click on the ‘Name‘ drop-down list
  3. Select‘android.permission.INTERNET

If you want to see what this has done to the underlying XML in the file then click on the ‘AndroidManifest.xml‘ tab and you should be able to see where these changes have been applied – the file is pretty small at this stage.

Now for the code. Double-click on the OpenMap file to open it in the code window and you’ll see that the wizard has generated some skeleton code – which will actually run but the results are not all that inspiring.

package com.onthefencedevelopment.androidmaps101;
 
import android.app.Activity;
import android.os.Bundle;
 public class OpenMap extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

We need to make some changes to this code to enable mapping so do the following:

  • Update the class definition so that it extends MapActivity rather than just Activity.

If you are using Eclipse it will now display some red squiggles under this new code indicating a problem.

  • Hover over the mouse over the squiggle and wait for Eclipse to display it’s info window detailing the problem and suggested solutions.
  • This problem is that we have not imported the MapActivty namespace so select the option to do so and move onto the the next problem.
  • Eclipse should now display another squiggle under the class name OpenMap. Again, hover the mouse over the squiggle and Eclipse will display the problem and list of solutions.
  • This issue it due to the fact that any class that extends MapActivity must implement the ‘IsRouteDisplayed’ method. Eclipse provides a solution of ‘Add Unimplemented Methods’ so click on that and the default method stub will be added which just returns a boolean value of false. This is fine for our purposes.

Your code should now look like this (note that I have also removed the import statement for Activity which we no longer need).

package com.onthefencedevelopment.androidmaps101;
 
import com.google.android.maps.MapActivity;
 
import android.os.Bundle;
 
public class OpenMap extends MapActivity {
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
   }
 
   @Override
   protected boolean isRouteDisplayed() {
       // TODO Auto-generated method stub
       return false;
   }
}

Believe it or not, this is all the code we need in here for the moment – but we are not quite done yet. When this code runs the OnCreate method will be called and this simply displays a view called main. So we need to update this view to display a map.

To open the view, expand the projects res folder followed by the layout folder. In here you’ll find a file called main.xml. Double-click on this file to open it.

Chances are that it will open in a graphical editor as below:

Currently the view just displays the good old ‘Hello World’ text – I told you it was uninspiring. We are going to remove all of this and replace it to display our map.

At the bottom of the screen there are two tabs, Graphical Layout and main.xml. This allows you to flick between the graphical and raw XML views. For this exercise we’ll drop into the XML to make our updates so click on the main.xml tab.

Replace the entire file content with the following code:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
 
    <com.google.android.maps.MapView
        android:id="@+id/map_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:enabled="true"
        android:clickable="true"
        android:apiKey="ADD YOUR API KEY HERE"
        />
 
    <LinearLayout android:id="@+id/zoom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        /> 
 
</RelativeLayout>

Without going into too much detail here, we’ve created a RelativeLayout which will fill the screen. This contains a MapView component called map_view (which will fill the above layout) and a LinearLayout called zoom which will contain the Zoom In/Out controls (which is ‘docked’ to the bottom of the main layout).

You will now need the API key you created at the start of this post and insert it into the apiKey element.

If you now flick back to the graphical view you’ll see that the result is still not that inspiring – if you were expecting a map to be displayed then you will be disappointed, but I have a cure for that. Presuming that you have a suitable AVD (an emulator configuration) you should now be able to hit F11 and run your application in debug mode. After the emulator has started up (it can take a few minutes to start from cold but be patient) you should see the application running and, by default, displaying a map centered on the United States.

As it stands you can move the map around but can’t zoom in or out – but a few lines of code will sort that out.

Back in Eclipse, go to the OpenMap file and enter the update it as below.

package com.onthefencedevelopment.androidmaps101;
 
import android.os.Bundle;
import com.google.android.maps.MapView;
 
// Add the following three import statements
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
 
public class OpenMap extends MapActivity {
 
    private MapController mapController;  // Add this line
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Add the following ----------------------------------------------
 
        // Get Mapping Controllers etc
        MapView mapView = (MapView) findViewById(R.id.map_view);
        mapController = mapView.getController();
 
        // Centre on Exmouth
        mapController.setCenter(new GeoPoint((int) (50.634955 * 1E6),
                                                  (int) (-3.409753 * 1E6)));
        mapController.setZoom(11);
        mapView.setBuiltInZoomControls(true);
 
        // ----------------------------------------------------------------
    }
 
    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }
}

Rerun the application and not only is the map now centered on Exmouth (my home town) but also zoomed in quite a bit more than before. Also, if you click on the map you’ll see that the zoom controls now appear – fading out after a few seconds.

So there we have it, we’ve written less than 100 lines of code and have a working base for our maps application.

In Part 2 we’ll add the ability to display markers (or pins) on the map to indicate some points of interest. Where we get this data from is entirely up to us – as long as we have latitude and longitude values we can stick a pin on the map.

If you’ve had any problems following along (or if I’ve missed something when writing this up) then I’ve uploaded the Eclipse project for you to download here.

Please feel free to leave a comment if you think I could improve this post – I’m not so proud that I can’t take it 😉