2Lines Software
20Jan/0915

Programming with Android Part 3 – Building WikiWhere

We will continue from where we left off in the last instalment and extend our basic application into a more useful application. If you are keeping track, we're an hour and fifteen minutes into our development and we have our application environment setup and ready to program in Android.

Over the next hour we will create an application that will show the wiki entries near our location, display that information on a map and embed the wiki page into the application. This application highlights three key features of the Android platform;

  1. Focus on LBS that makes locative media easier to produce.
  2. Deep web integration from consuming web services to embedding web page.
  3.  Simple map integration via Google Maps.

What is WikiWhere

WikiWhere is a buzzword-compliant mobile application that gives you access to local wiki knowledge where ever you are. Using your Android phone, WikiWhere searches wiki for entries near your location and passes that information to you. Discover your neighbourhood or visit others. 

1:20 Back from Break. You aren't paid to sit there you know.

We are now going to get started with the WikiWhere application. Let's go through the first step and create an application shell using the Eclipse project builder. Just as in the previous part, select File >> New Project >> Android Project to launch a new project. Add the details for WikiWhere. I've attached a screenshot of the details below.

WikiWhere Project Dialog

Step 1. Create the MapView

The main user interface will be a MapView so we want to change the interface of our WikiWhere class from Activity to MapActivity and implement the onRouteDisplayed method.

  1. package demo.WikiWhere;
  2.  
  3. import com.google.android.maps.MapActivity;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6.  
  7. public class WikiWhere extends MapActivity {
  8.     public void onCreate(Bundle savedInstanceState) {
  9.  
  10.         super.onCreate(savedInstanceState);
  11.         setContentView(R.layout.main);
  12.  
  13.     }  
  14.    
  15.      protected boolean isRouteDisplayed() {
  16.          return false;
  17.      }
  18. }

Next we have to implement the map in the view. Android separates the device layout / views from the source code to simplify development. A graphic designer can modify the XML layout without impacting the underlying source code. I'm a strong believer in letting programmers do the programming and not the UI or graphic design. Windows 3.1 was designed by programmers. Case closed.

The layouts are located in the res/layout folder in the project. We will edit the main.xml file and replace it with the following code:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.         android:orientation="vertical" android:layout_width="fill_parent"
  4.         android:layout_height="fill_parent">
  5. <Button android:id="@+id/search" android:layout_width="fill_parent"
  6.         android:layout_height="wrap_content" android:text="Search For Wiki Entries"/>
  7.  
  8. <com.google.android.maps.MapView
  9.          android:id="@+id/map" android:layout_width="fill_parent"
  10.          android:layout_height="wrap_content" android:layout_below="@+id/search"
  11.          android:apiKey="<strong>YOUR-GOOGLE-MAPS-API-KEY</strong>"
  12.          android:enabled="true" android:clickable="true"/>
  13.  
  14. <LinearLayout android:id="@+id/zoom_holder"
  15.          android:layout_width="fill_parent" android:layout_height="fill_parent"
  16.          android:gravity="bottom|center_horizontal" />
  17. </RelativeLayout>

This layout file adds three components. At the top will be a button to search for wiki entries. The main view is a Google Maps view. And finally, the bottom component is a holder for the zoom in/out control on the map. You will have to apply for your own Google Maps key and you can do that from this site

Step 2. Permissions

The next step is to tell Android that we need access to the maps library and permission to access the internet from the device. All the configurations is located in the AndroidManifest.xml file. This file controls all the permissions and OS access for the application. It would be worth your while to become very familiar with that file. The Android/Eclipse toolkit provides a graphical interface to simplify the editing of the file. Personally, I prefer to use the raw xml (click on the far right tab when in the graphical interface) but really its up to you.

The first step is to add the library to the application. Add the <uses-library tag after the activity tag and before the application tag. Like this;

  1.         </activity>
  2. <uses-library android:name="com.google.android.maps"></uses-library>
  3. <application/>

Next, we will add the permissions to the file:

  1. ..</application>
  2. <uses-permission android:name="android.permission.INTERNET"></uses-permission>

This is all the changes that need to occur to get a MapView running on the device. If you setup a Run Configuration identical to Part 2 and run the application in the emulator you should see this;

WikiWhere Map View

That's it for adding a draggable map into your application. Click and drag the map around.

Step 3. Adding Extra Components

As a final step we will add a zoom button and center the map on a closer area. Add this code to your WikiWhere.java file;

  1. private double START_LAT = 51.066832;
  2. private double START_LNG =-114.086108;
  3. private int START_ZOOM = 15;
  4.  
  5. private MapController mc;
  6.  
  7. private MapView mv;
  8.  
  9. private View zoomView;
  10.  
  11. public void onCreate(Bundle savedInstanceState) {
  12.            super.onCreate(savedInstanceState);
  13.  
  14.            setContentView(R.layout.main);
  15.  
  16.            initMap();
  17.  
  18. }
  19.  
  20. private void initMap(){
  21.  
  22.             mv = (MapView)findViewById(R.id.map);
  23.             mc = mv.getController();
  24.  
  25.              // Initialise the map
  26.  
  27.              mc.setZoom(START_ZOOM);
  28.              mc.setCenter(new GeoPoint((int)(START_LAT*1E6),(int)(START_LNG*1E6)));
  29.              mv.setSatellite(true);
  30.              mv.setStreetView(false);
  31.              
  32.              LinearLayout zoom = (LinearLayout)findViewById(R.id.zoom_holder);
  33.              zoomView = mv.getZoomControls();
  34.              zoomView.setLayoutParams(
  35.                           new ViewGroup.LayoutParams(
  36.                                        ViewGroup.LayoutParams.WRAP_CONTENT,
  37.                                        ViewGroup.LayoutParams.WRAP_CONTENT
  38.                           )
  39.              );
  40.  
  41.              zoom.addView(zoomView);
  42.              mv.displayZoomControls(true);
  43. }

This adds a zoom panel at the bottom, centers the map on a location (Kits Bar in Calgary, AB. home to MobileMonday Calgary every month) and adds a satellite overview to the map. Go ahead run the app again. Pretty cool, huh?

1:40 Accessing the On-Device Location

This next section deals with getting the devices current location from GPS. Android provides a simple interface through the LocationManager to get the location. An important differentiator of the LocationManager is that it listens to the location stream and therefore allows many applications to access the GPS on the device. Many other GPS implementations on mobile devices don't allow multiple applications to access the location. This is a major drawback to running background applications since the OS would dictate which applications receive the location and which ones are stopped. This architecture also means that the time for a location fix is drastically reduced in the device. I will be writing an article outlining the importance of this architecture in an upcoming article on the Android GPS accuracy.

Either way, back to WikiWhere. Androids permission scheme requires that the permissions for GPS and in fact all location types be entered into the AndroidManifest.xml first.

Add the following lines under the other <uses-permissions tag from the previous section:

  1. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
  2. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
  3. <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"></uses-permission>

As I mentionned above the GPS is pushed through a LocationListener. We will be implementing that interface in the WikiWhere class and extending the LocationListener methods in the code.

  1.  public class WikiWhere extends MapActivity implements LocationListener {

Initializing the LocationManager is done by calling requestLocationUpdates

  1. private LocationManager lm;
  2. private MyLocationOverlay lo;
  3.  
  4. public void onCreate(Bundle savedInstanceState) {
  5.            super.onCreate(savedInstanceState);
  6.  
  7.            setContentView(R.layout.main);
  8.  
  9.            initMap();
  10.  
  11.            initLocation();
  12. }
  13.  
  14. private void initLocation(){
  15.  
  16.            lm= (LocationManager) this.getSystemService(LOCATION_SERVICE);
  17.            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 500, this);
  18.  
  19. }

The requestLocationUpdates allows you to tailor your listener so you only get updates when a user has moved a certain distance and/or a certain time has passed. In our example above we are getting locations every 5 seconds (5000 ms) or when the user moves more than 500m. Location objects will be pushed to the onLocationChangedMethod whenever updates are available.

For the next part we will create an Overlay to display the users position. Overlays are Androids way of handle icons on a map. There is a default overlay called MyLocationOverlay that is specifically designed to handle the users location updates from GPS. If you were so inclined feel free to write your own map Overlay for the users locations. (We will be doing that later for the Wiki Points).

  1. public void onLocationChanged(Location location) {
  2.  
  3.            List overlays = mv.getOverlays();
  4.            lo = new MyLocationOverlay(this,mv);
  5.            overlays.add(lo);
  6.            lo.enableMyLocation();
  7. }

That code will update the location and display it on the map. Save and run the application on the emulator.

The first thing you will notice is that there is no flashing blue icon on the map. That is because no location has been sent to the emulator. The Android SDK provides a toolkit to help you out here. Select Window>>Open Perspective>>DDMS (You may have to click other and browse for it, if it isn't there). This perspective gives you a large collection of tools to help optimize, debug and monitor applications on the emulator (and devices!). Under the Emulator Control tab scroll down until you see the Location Controls.

DDMS Location Controls

 This  control allows you to push one location or a series of locations in GPX or KML format. A fantastic tool for testing applications. Enter the latitude and longitude into the box and click send. This will simulate a GPS location coming into the device and you should immediately see the flashing blue icon in your emulator.

Showing My Location

Locations are automatically updated everytime you move. Try playing around with the DDMS Location Control and see how it moves your position around dynamically.

 That is it for the first part of WikiWhere. In the next part we will learn how to call web services, simple parsing of XML and of course the WebView. See you then!

 

 Final Part

 

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
Comments (15) Trackbacks (2)
  1. Excellent map tutorial.. Thank you very much for giving this nice tutorial and example..

    Thanks & Regards,
    Bose.C

  2. Yes! Very cool this tutorial…

    The only thing that doesn’t work is the flashing blue icon although I push the data via the DDMS window. I also get a warning in my eclipse for the onLocationChanged(Location location) Method…

    Any idea???

  3. Are you running android 1.1? I suspect I might have some compatibility issues as other people have indicated.

    Can you relay what warning you are getting in Eclipse?

  4. Yes. I’m using the “android-sdk-linux_x86-1.1_r1″ Does the compatibility issues includes the (missing) flashing blue icon?

    The first line in the onLocationChanged Method gives me the warning:
    1.
    “Add type parameters to list” with the suggestion:
    public void onLocationChanged(Location location) {
    List overlays = mv.getOverlays();
    lo = new MyLocationOverlay(this,mv);
    2.
    “Infer Generic Type Arguments” with the suggestion:
    Start the ‘Infer Generic Type Arguments’ refactoring
    3.
    @SuppressWarnings(“unchecked”)

    Then in the same method the line
    overlays.add(lo);
    gives the warnings:
    1. Add type parameters to ‘List’
    2. Extract to local variable (replace all occurrences)
    3. Extract to local variable
    4. Infer Generic Type Arguments…
    5. Extract to method
    6. Assign statement to new local variable
    7. Assign statement to new field
    8. Add @SuppressWarnings ‘unchecked’ to ‘onLocationChanged’

    I’m sorry, but i’m an absolute beginner. In Android as in Java. So I can’t understand anything in this error/warning messages…

    Hope for you help!

  5. No idea? Nobody else has the same problem?

  6. Hi, i’m runnig it on sdk 1.5 but not able to use the emulator control..as a matter of fact no icon appears when I click on send Langitude ,Latitude, what could be the problem?

  7. Hi Lorenz,

    Unfortunately there is a DDMS bug in the emulator for 1.5 (at least up to r3 of the SDK). The GPS locations are not loading in the device. Occasionally I was able to get a single location sent to the device. GPX files sent one location and stopped. Hopefully they will fix it in the new release. Until then I have two copies of eclipse running, one with 1.1 for testing and 1.5 for development.

  8. Excellent tutorial. Please, is there a way I can also send entries to the wiki server? For instance, if I want to write something about a place, how do I do that with this application. Thanks

  9. Hi, i cant seem to see the blue dot

  10. I think the problem is that the DDMS doesn’t work properly. I am just printing the coords (by using location.getLatitude()) and it keeps on saying 0.0.
    Except for when I use location.setLatitude(543) but that’s not dynamic of coursse. :P

  11. Have you upgraded to the 1.6 version of the SDK? There was a bug in 1.5 r1 in that the coordinates would only occasionally get through.

  12. I have Android 1.6.

    Here’s the main code:

    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    lm = (LocationManager) this.getSystemService(LOCATION_SERVICE);
    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1, 1, this);

    a = (TextView) findViewById(R.id.TextView01);
    b = (TextView) findViewById(R.id.TextView02);
    }

    @Override
    public void onLocationChanged(Location location) {
    double w = location.getLatitude();
    double e = location.getLongitude();
    a.setText(String.valueOf(w));
    b.setText(“long ” + String.valueOf(e));
    newLoc();
    }

  13. That’s a good question why it isn’t working. Can you try selecting the emulator in the DDMS window? If you run multiple instances of the emulator and devices it can often route the connection to another device or emulator.

    I tested out your code by importing it into another project (you can download it here if it helps) and it works out with the DDMS emulator control.

    Do you have and output of the LogCat? That may tell you more about where the error might be occuring.

  14. This is what my emulator says:

    [2009-12-18 11:17:23 - AdaptaGPS]——————————
    [2009-12-18 11:17:23 - AdaptaGPS]Android Launch!
    [2009-12-18 11:17:23 - AdaptaGPS]adb is running normally.
    [2009-12-18 11:17:23 - AdaptaGPS]Performing org.me.GetLoc activity launch
    [2009-12-18 11:17:23 - AdaptaGPS]Automatic Target Mode: launching new emulator with compatible AVD ‘QVGA’
    [2009-12-18 11:17:23 - AdaptaGPS]Launching a new emulator with Virtual Device ‘QVGA’
    [2009-12-18 11:17:25 - AdaptaGPS]New emulator found: emulator-5554
    [2009-12-18 11:17:25 - AdaptaGPS]Waiting for HOME (‘android.process.acore’) to be launched…
    [2009-12-18 11:18:18 - AdaptaGPS]HOME is up on device ‘emulator-5554′
    [2009-12-18 11:18:18 - AdaptaGPS]Uploading AdaptaGPS.apk onto device ‘emulator-5554′
    [2009-12-18 11:18:19 - AdaptaGPS]Installing AdaptaGPS.apk…
    [2009-12-18 11:18:40 - AdaptaGPS]Success!
    [2009-12-18 11:18:40 - AdaptaGPS]Starting activity org.me.GetLoc on device
    [2009-12-18 11:18:55 - AdaptaGPS]ActivityManager: Starting: Intent { cmp=org.me/.GetLoc }

  15. I am not getting the flashing blue icon on wiki where. I doubt if dere are soe lines missing in the follwing function. Afterall we are not adding location to overlays….please suggest a way to get blue icons on my map.
    public void onLocationChanged(Location location) {
    List overlays = mv.getOverlays();
    lo = new MyLocationOverlay(this,mv);
    overlays.add(lo);
    lo.enableMyLocation();
    }


Leave a comment