Learning Resources
 

Google maps, search and LBS in Android


Google Maps in Android

The Android platform provides easy and tight integration between Android applications and Google Maps. The well established Google Maps API is used under the hood in order to bring the power of Google Maps to your Android applications. In this tutorial we will see how to incorporate Google Maps into an Android app.

Installing the Google APIs

In order to be able to use Google Maps, the Google APIs have to be present in your SDK. In case the Google APIs are not already installed, you will have to manually install them. This is accomplished by using the Android SDK and AVD Manager.

Launch the manager and choose the “Installed Options” section to see what is already installed and the “Available Packages” to download the additional APIs.



You can find more information about this procedure in the following links:

    Adding SDK Components
    Installing the Google APIs Add-On


Setting up an Eclipse project

Now that the appropriate tools are installed, let's proceed with creating a new Android project in Eclipse. The project I created is named “AndroidGoogleMapsProject” and has the following configuration:



It is important to use the “Google APIs” as the target since this option includes the Google extensions that allow you to use Google Maps. Return to the first step of this tutorial if no such option is available in your configuration. I chose the 1.5 version of the platform since we will not be using any of the latest fancy API stuff.

Google Maps API Key Generation

As you might know if you have used the Google Maps API in the past, a key is required in order to be able to use the API. The process is slightly different for use in Android applications, so let's see what is required to do. Note that the process is described in the “Obtaining a Maps API Key” page, but I will also provide a description here.

First, we have to calculate the MD5 fingerprint of the certificate that we will use to sign the final application. This fingerprint will have to be provided to the Google Maps API service so that it can associate the key with your application. Java's Key and Certificate Management tool named keytool is used for the fingerprint generation.

The keytool executable resides in the %JAVA_HOME%/bin directory for Windows or $JAVA_HOME/bin for Linux/OS X. For example, in my setup, it is installed in the “C:\programs\Java\jdk1.6.0_18\bin” folder.

While developing an Android application, the application is being signed in debug mode. That is, the SDK build tools automatically sign the application using the debug certificate. This is the certificate whose fingerprint we need to calculate. To generate the MD5 fingerprint of the debug certificate we first need to locate the debug keystore. The location of the keystore varies by platform:

    Windows Vista: C:\Users\\.android\debug.keystore
    Windows XP: C:\Documents and Settings\\.android\debug.keystore
    OS X and Linux: ~/.android/debug.keystore


Now that we have located the keystore, we use the keytool executable to get the MD5 fingerprint of the debug certificate by issuing the following command:

keytool -list -alias androiddebugkey \
-keystore .keystore \
-storepass android -keypass android

For example, in my Windows machine I changed directory to the .android folder and I used the following command:

%JAVA_HOME%/bin/keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android

Note that this was executed against the debug keystore, you will have to repeat this for the keystore that will be used with the application you are going to create. Additionally, the application is run on another development environment, with different Android SDK keystore, the API key will be invalid and Google Maps will not work.

The output would be something like the following:

androiddebugkey, Apr 2, 2010, PrivateKeyEntry,
Certificate fingerprint (MD5): 72:BF:25:C1:AF:4C:C1:2F:34:D9:B1:90:35:XX:XX:XX

This the fingerprint we have to provide to the Google Maps service. Now we are ready to sign up for a key by visiting the Android Maps API Key Signup page. After we read and accept the terms and conditions, we provide the generated fingerprint as follows:



We generate the API key and we are presented with the following screen:



Creating the Google Maps application

Finally, its time to write some code. Bookmark the Google APIs Add-On Javadocs for future reference. Integrating Google Maps is quite straightforward and can be achieved by extending the MapActivity class instead of the Activity class that we usually do. The main work is performed by a MapView which displays a map with data obtained from the Google Maps service. A MapActivity is actually a base class with code to manage the boring necessities of any activity that displays a MapView. Activity responsibilities include:

    Activity lifecycle management
    Setup and teardown of services behind a MapView


To extend from MapActivity we have to implement the isRouteDisplayed method, which denotes whether or not we are displaying any kind of route information, such as a set of driving directions. We will not provide such information, so we just return false there.

In our map activity, we will just take reference of a MapView. This view will be defined in the layout XML. We will also use the setBuiltInZoomControls method to enable the built-in zoom controls.

Let's see how our activity looks like so far:


package com.javacodegeeks.android.googlemaps;

import android.os.Bundle;

import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;

public class GMapsActivity extends MapActivity {
    
    private MapView mapView;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.map_view);       
        mapView.setBuiltInZoomControls(true);
        
    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }
    
}


Let's also see the referenced main.xml layout file:



 xmlns:android="https://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 
    xmlns:android="https://schemas.android.com/apk/res/android"
  android:id="@+id/map_view"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:clickable="true"
  android:enabled="true"
  android:apiKey="API-KEY-HERE" />
   



Do not forget to provide your API key in the relevant field or else Google Maps will not work.

Launching the application

To test the application we will have to use a device that includes the Google APIs. We will use the AVD manager to create a new device with target set to one of the Google APIs and settings like the following:



If we now launch the Eclipse configuration, we will encounter the following exception:

java.lang.ClassNotFoundException: com.javacodegeeks.android.googlemaps.GMapsActivity in loader dalvik.system.PathClassLoader@435988d0

The problem is that we haven't notified Android that we wish to use the add-on Google APIs which are external to the base API. To do so, we have to use the uses-library element in our Android manifest file, informing Android that we are going to use classes from the com.google.android.maps package.

Additionally, we have to grant internet access to our application by adding the android.permission.INTERNET directive. Here is how our AndroidManifest.xml file looks like:



      package="com.javacodegeeks.android.googlemaps"
      android:versionCode="1"
      android:versionName="1.0">
      
   
    
                          android:label="@string/app_name">
           
               
               
           

       

        
     
      
   


   




And here is what the application screen looks like:



If you click inside the map, the zoom controls will appear and you will be able to zoom in and out.

Adding map overlays

The next step is to add some custom map overlays. To do so, we can extend the Overlay class, which is a base class representing an overlay which may be displayed on top of a map. Alternatively, we may extend the ItemizedOverlay, which is a base class for an Overlay which consists of a list of OverlayItems. Let's see how we can do this (note that the following example is very similar to the Hello Map View article from the Android documentation):

package com.javacodegeeks.android.googlemaps;

import java.util.ArrayList;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;

import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;

public class CustomItemizedOverlay extends ItemizedOverlay {
   
   private ArrayList mapOverlays = new ArrayList();
   
   private Context context;
   
   public CustomItemizedOverlay(Drawable defaultMarker) {
        super(boundCenterBottom(defaultMarker));
   }
   
   public CustomItemizedOverlay(Drawable defaultMarker, Context context) {
        this(defaultMarker);
        this.context = context;
   }

   @Override
   protected OverlayItem createItem(int i) {
      return mapOverlays.get(i);
   }

   @Override
   public int size() {
      return mapOverlays.size();
   }
   
   @Override
   protected boolean onTap(int index) {
      OverlayItem item = mapOverlays.get(index);
      AlertDialog.Builder dialog = new AlertDialog.Builder(context);
      dialog.setTitle(item.getTitle());
      dialog.setMessage(item.getSnippet());
      dialog.show();
      return true;
   }
   
   public void addOverlay(OverlayItem overlay) {
      mapOverlays.add(overlay);
       this.populate();
   }

}


Our class requires an Android Drawable in its constructor, which will be used as a marker. Additionally, the current Context has to be provided. We use an ArrayList to store all the OverlayItems stored in the specific class, so the createItem and size methods are pretty much self-explanatory. The onTap method is called when an item is “tapped” and that could be from a touchscreen tap on an onscreen Item, or from a trackball click on a centered, selected Item. In that method, we just create an AlertDialog and show it to the user. Finally, in the exposed addOverlay method, we add the OverlayItem and invoke the populate method, which is a utility method to perform all processing on a new ItemizedOverlay.

Let's see how this class can be utilized from our map activity:

package com.javacodegeeks.android.googlemaps;

import java.util.List;

import android.graphics.drawable.Drawable;
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.Overlay;
import com.google.android.maps.OverlayItem;

public class GMapsActivity extends MapActivity {
    
    private MapView mapView;
    
    private static final int latitudeE6 = 37985339;
    private static final int longitudeE6 = 23716735;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.map_view);       
        mapView.setBuiltInZoomControls(true);
        
        List mapOverlays = mapView.getOverlays();
        Drawable drawable = this.getResources().getDrawable(R.drawable.icon);
        CustomItemizedOverlay itemizedOverlay =
             new CustomItemizedOverlay(drawable, this);
        
        GeoPoint point = new GeoPoint(latitudeE6, longitudeE6);
        OverlayItem overlayitem =
             new OverlayItem(point, "Hello", "I'm in Athens, Greece!");
        
        itemizedOverlay.addOverlay(overlayitem);
        mapOverlays.add(itemizedOverlay);
        
        MapController mapController = mapView.getController();
        
        mapController.animateTo(point);
        mapController.setZoom(6);
        
    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }
    
}


We create a new instance of our CustomItemizedOverlay class by using the default Android icon as the Drawable. Then we create a GeoPoint pointing to a predefined location and use that to generate an OverlayItem object. We add the overlay item to our CustomItemizedOverlay class and it magically appears in our map on the predefined point.

Finally, we take reference of the underlying MapController and use it to point the map to a specific geographical point using the animateTo method and to define the zoom level by using the setZoom method.

If we launch again the configuration, we will be presented with a zoomed-in map which includes an overlay marker pointing to JavaCodeGeeks home town Athens, Greece. Clicking on the marker will cause the alert dialog to pop-up displaying our custom message.

Search in Android


Search is a core user feature on Android. Users should be able to search any data that is available to them, whether the content is located on the device or the Internet. To help create a consistent search experience for users, Android provides a search framework that helps you implement search for your application.

Figure 1. Screenshot of a search dialog with custom search suggestions.

The search framework offers two modes of search input: a search dialog at the top of the screen or a search widget (SearchView) that you can embed in your activity layout. In either case, the Android system will assist your search implementation by delivering search queries to a specific activity that performs searchs. You can also enable either the search dialog or widget to provide search suggestions as the user types. Figure 1 shows an example of the search dialog with optional search suggestions.

Once you've set up either the search dialog or the search widget, you can:

  • Enable voice search
  • Provide search suggestions based on recent user queries
  • Provide custom search suggestions that match actual results in your application data
  • Offer your application's search suggestions in the system-wide Quick Search Box

Note: The search framework does not provide APIs to search your data. To perform a search, you need to use APIs appropriate for your data. For example, if your data is stored in an SQLite database, you should use the android.database.sqliteAPIs to perform searches.

Also, there is no guarantee that every device provides a dedicated SEARCH button to invoke the search interface in your application. When using the search dialog or a custom interface, you must always provide a search button in your UI that activates the search interface. For more information, see Invoking the search dialog.

The following documents show you how to use Android's framework to implement search:

Creating a Search Interface
How to set up your application to use the search dialog or search widget.
Adding Recent Query Suggestions
How to provide suggestions based on queries previously used.
Adding Custom Suggestions
How to provide suggestions based on custom data from your application and also offer them in the system-wide Quick Search Box.
Searchable Configuration
A reference document for the searchable configuration file (though the other documents also discuss the configuration file in terms of specific behaviors).

Protecting User Privacy


When you implement search in your application, take steps to protect the user's privacy. Many users consider their activities on the phone—including searches—to be private information. To protect each user's privacy, you should abide by the following principles:

  • Don't send personal information to servers, but if you must, do not log it.

    Personal information is any information that can personally identify your users, such as their names, email addresses, billing information, or other data that can be reasonably linked to such information. If your application implements search with the assistance of a server, avoid sending personal information along with the search queries. For example, if you are searching for businesses near a zip code, you don't need to send the user ID as well; send only the zip code to the server. If you must send the personal information, you should not log it. If you must log it, protect that data very carefully and erase it as soon as possible.

  • Provide users with a way to clear their search history.

    The search framework helps your application provide context-specific suggestions while the user types. Sometimes these suggestions are based on previous searches or other actions taken by the user in an earlier session. A user might not wish for previous searches to be revealed to other device users, for instance, if the user shares the device with a friend. If your application provides suggestions that can reveal previous search activities, you should implement the ability for the user to clear the search history. If you are using SearchRecentSuggestions, you can simply call the clearHistory()method. If you are implementing custom suggestions, you'll need to provide a similar "clear history" method in your content provider that the user can execute.

Location based Services in Android


Location and maps-based applications are compelling for mobile device users. You can build these capabilities into your applications using the classes of the android.locationpackage and the Google Maps external library. The sections below provide details.

Location Services


Android gives your applications access to the location services supported by the device through the classes in the android.locationpackage. The central component of the location framework is the LocationManagersystem service, which provides APIs to determine location and bearing of the underlying device (if available).

As with other system services, you do not instantiate a LocationManagerdirectly. Rather, you request an instance from the system by calling getSystemService(Context.LOCATION_SERVICE). The method returns a handle to a new LocationManagerinstance.

Once your application has a LocationManager, your application is able to do three things:

  • Query for the list of all LocationProviders for the last known user location.
  • Register/unregister for periodic updates of the user's current location from a location provider (specified either by criteria or name).
  • Register/unregister for a given Intentto be fired if the device comes within a given proximity (specified by radius in meters) of a given lat/long.

For more information, read the guide to Location Strategies.

Google Maps External Library


To make it easier for you to add powerful mapping capabilities to your application, Google provides a Maps external library that includes the com.google.android.maps package. The classes of the com.google.android.maps package offer built-in downloading, rendering, and caching of Maps tiles, as well as a variety of display options and controls.

The key class in the Maps package is com.google.android.maps.MapView, a subclass of ViewGroup. A MapView displays a map with data obtained from the Google Maps service. When the MapView has focus, it will capture keypresses and touch gestures to pan and zoom the map automatically, including handling network requests for additional maps tiles. It also provides all of the UI elements necessary for users to control the map. Your application can also use MapView class methods to control the MapView programmatically and draw a number of Overlay types on top of the map.

In general, the MapView class provides a wrapper around the Google Maps API that lets your application manipulate Google Maps data through class methods, and it lets you work with Maps data as you would other types of Views.

The Maps external library is not part of the standard Android library, so it may not be present on some compliant Android-powered devices. Similarly, the Maps external library is not included in the standard Android library provided in the SDK. So that you can develop using the classes of the com.google.android.maps package, the Maps external library is made available to you as part of the Google APIs add-on for the Android SDK.

 

--Google