Working with the Amazon Maps API on the Kindle Fire

Revision as of 14:45, 5 May 2016 by Neil (Talk | contribs) (Text replacement - "<table border="0" cellspacing="0" width="100%">" to "<table border="0" cellspacing="0">")

Revision as of 14:45, 5 May 2016 by Neil (Talk | contribs) (Text replacement - "<table border="0" cellspacing="0" width="100%">" to "<table border="0" cellspacing="0">")

PreviousTable of ContentsNext
Kindle Fire Audio Recording and Playback using MediaPlayer and MediaRecorderA Kindle Fire Amazon Maps API Tutorial


<google>BUY_KINDLE_FIRE</google>


One can argue with a reasonable degree of certainty that Google has created one of the best and most accurate digital map services available on the market today. In the early versions of iOS, Apple made the wise decision to rely on Google Maps for navigation on the iPhone. Rather than continue to use Google Maps, however, Apple instead invested a considerable amount of effort in developing a replacement to Google Maps in iOS 6. The result was an unreliable service that became a public relations problem for Apple and resulted in the departure of a number of senior Apple executives.

When a mobile device manufacturer chooses to use Android as the operating system for a new device, Google Maps comes bundled as part of the overall package. The typical Android based phone or tablet, therefore, includes a Google Maps application and provides access to the Google Maps API for use by app developers. This, however, is not the case for the Kindle Fire. Amazon has, instead, removed Google Maps from both the operating system and the SDK, and replaced it with the Amazon Maps system.

This aversion to using the Google Maps system on the part of Apple and Amazon is not entirely irrational. Consider, for example, that the provider of the map service on a mobile device gets to learn a great deal of information about a user. Each time a user accesses the map system, the provider finds out not only where the user is, but also where they might be going. Understandably, neither Apple nor Amazon were comfortable letting Google (in many ways a competitor) have this level of information about their customers.

This chapter is intended to provide an overview of the Amazon Maps system and API. Once the basics have been covered, the following chapters will work through some tutorials demonstrating the use of this API.

Amazon Maps vs. Google Maps

When Amazon took the decision not to use Google Maps on the Kindle Fire it became evident that this might present a problem for the large number of existing Android applications that would potentially need to be migrated to the Kindle Fire. In recognition of this fact, the Amazon Maps API largely mirrors that of Google Maps. Most existing Google Maps code will, therefore, migrate over to Amazon Maps without requiring a significant amount of work.

One key issue, however, is that Amazon Maps lacks some of the main features of Google Maps, street views and traffic information being two notable examples. Whilst the Amazon Maps API includes these API features so that existing code will compile, they do nothing when called by the application.

The Elements of Amazon Maps

The Amazon Maps API consists of a core set of classes that combine to provide mapping capabilities in Android applications on the Kindle Fire. The key classes are:

  • MapActivity – A subclass of the Android Activity class, this provides the base class for activities that need to provide map support. Any activity that needs to work with maps must be derived from this class.
  • MapView - Provides the canvas onto which the map is drawn.
  • MapController – Provides an interface for managing an existing map. This class includes capabilities such as setting both the center coordinates of the map and the current zoom level.
  • ItemizedOverlay – A class specifically designed to overlay information onto a map. For example, an overlay might be used to mark all the locations of the public libraries in a town. A single overlay can contain multiple items, each represented by an OverlayItem instance. An onTap() callback method may be implemented to pop up additional information about a location when tapped by the user.
  • OverlayItem – Used to represent each item in an ItemizedOverlay. Each item has associated with it a location on the map and an optional image to mark the location.
  • MyLocationOverlay – A special-purpose overlay designed specifically to display the current location of the device on the map view.
  • Overlay – A general-purpose overlay class provided primarily to allow transparent effects or content to be placed on top of the map.

Getting Ready to Use Amazon Maps

The use of Amazon Maps in an application is somewhat unusual since there is more work involved in setting up the environment than there is in actually writing the Java code. Each step must be performed carefully to ensure that maps will function within an application.

Downloading the Amazon Mobile SDK

Amazon Maps are part of the Amazon Mobile SDK, which will need to be downloaded and integrated into any Eclipse project for which maps are to be included. The SDK can be downloaded using the following link:

https://developer.amazon.com/sdk/download/sdk.html

Once downloaded, unzip the archive into a suitable location.

Adding the Amazon Mobile SDK to an Eclipse Project

The Maps SDK JAR file will need to be added to the build path of any application that requires map functionality. To add map support to a project, locate it within the Eclipse Package Explorer panel, right click on the project name and select the Properties option from the resulting menu. Within the Properties dialog, select the Java Build Path category from the left hand panel and, within the Java Build Path panel, click on the Add External JARS… button. In the JAR selection dialog, navigate to the following location (where <sdk path> is replaced by the location on your file system where the Amazon Mobile SDK was installed in the previous step):

<sdk path>/Maps/lib

From within the libs directory, select the amazonmaps jar file. At time of writing, this file is named amazonmaps-1.0.2.jar but may have been superseded since then by a newer version. Once selected, click on the Open button to add the JAR file to the project. Once added, it should be listed in the Java Build Path panel as illustrated in Figure 40-1:


Configuring the Eclipse Java Build Path

Figure 40-1


Assuming the JAR file is now listed, click on OK to close the Properties dialog.

Obtaining Your Developer Signature

Before an application can make use of the Amazon Maps API, it must first be registered in the Amazon Mobile App Distribution portal. Before an app can be registered, however, the developer signature (also referred to as the MD5 debug fingerprint) associated with your development environment must be obtained. This is achieved by running the keytool utility that is supplied in the bin directory of the Java Development Kit (JDK) installed on the development system as outlined in Setting up a Kindle Fire Android Development Environment. One of the arguments passed to the keytool utility is the path to a file named debug.keystore. To find the location of this file, select the Eclipse Windows -> Preferences menu option and in the resulting dialog select Android -> Build from the left hand panel. In the Build Settings panel, the location of the file can be found in the Default debug keystore: field. Once the location has been identified, execute the following command within a terminal or command prompt window (where <key path> is replaced by the path to the debug.keystore file):

keytool -v -list -alias androiddebugkey -keystore <key path> -storepass android 

Upon execution, the above command will generate output similar to the following example:

Alias name: androiddebugkey
Creation date: Nov 30, 2011
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 503f6f0b
Valid from: Wed Nov 30 13:26:24 EST 2011 until: Fri Nov 22 13:26:24 EST 2041
Certificate fingerprints:
         MD5:  DF:86:AB:19:DC:28:BF:62:4C:49:82:6E:BA:77:45:B4
         SHA1: 6F:AD:25:3F:90:56:6C:9B:7D:29:95:54:AF:E3:E0:29:64:DB:BD:22
         SHA256: B8:3B:C7:43:4A:0A:77:E6:38:E1:66:18:E4:FF:EE:AA:55:66:88:99:F6:
6B:16:11:4D:E9:DA:DD:4E:0F:D0:B8
         Signature algorithm name: SHA256withRSA
         Version: 3

The MD5 fingerprint is the sequence of hexadecimal number pairs on the MD5: line of the output.

Registering the Application in the Amazon Mobile App Distribution Portal

The next step is to register the application in the Amazon distribution portal and input the MD5 debug fingerprint to enable Map support. To achieve this, open a web browser and navigate to the following URL:

https://developer.amazon.com/welcome.html

On the welcome page, click on the Sign In link in the top right hand corner of the page and enter your login credentials. If you do not yet have a developer account, click on the Create an Account button to create one now.

Once logged in, click on the Add a New App button located within the dashboard panel as shown in Figure 40-2:


The Amazon Distribution Portal Dashboard

Figure 40-2


Within the New App Submission screen, enter the mandatory information (those fields marked by a red asterisk) before clicking on the Save button.

Once the app has been added, select it from the dashboard panel to view the details of the submission. Within the application details screen will be a link labeled Maps. Click on this link and, in the resulting screen, click on the Add a Debug Registration button. When prompted, enter the package name for the application exactly as it appears within the project and then cut and paste the MD5 fingerprint into the Developer Signature field before clicking on the Submit button.

At this point, the development environment is set up to enable Maps to be used within a specific application. The next step is to set up the application itself to use maps. This begins with making some additions to the application’s Android manifest file.

Adding Map Support to the AndroidManifest.xml File

Before maps can be used in an application, an additional entry needs to be added to the application’s Android Manifest file. Within Eclipse, locate the manifest file for the project for which the Maps JAR file was added to the build path and load the AndroidManifest.xml file into the editing panel. The line that needs to be added reads as follows:

xmlns:amazon="http://schemas.amazon.com/apk/res/android"

This directive needs to be added as part of the existing <manifest> element. For example:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:amazon="http://schemas.amazon.com/apk/res/android"
    package="com.example.mapapp"
    android:versionCode="1"
    android:versionName="1.0" >
.
.

In addition, the Amazon Maps API requires that a number of permissions be requested within the manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:amazon="http://schemas.amazon.com/apk/res/android"
    package="com.example.mapapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-permission android:name= "android.permission.INTERNET" />
    <uses-permission android:name=
              "android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name= 
              "android.permission.ACCESS_FINE_LOCATION" />
.
.

Finally, the application element of the Manifest file must include the following tag:

<amazon:enable-feature android:name="com.amazon.geo.maps" android:required="false" />

For example:

.
.
.
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <amazon:enable-feature android:name="com.amazon.geo.maps" android:required="false" />
        <activity
            android:name="com.example.mapapp.MapAppActivity"
            android:label="@string/app_name" >
.
.
.

Enabling Location Based Services on the Kindle Fire Device

By default, Kindle Fire devices are shipped with location based services disabled. Before testing a map-based application on a physical device, therefore, this feature must first be enabled. To do this, display the settings app on the device (via a downward swipe from the top edge of the screen). Select the More option followed by Location Based Services. Within the location settings screen (Figure 40 3) change the Enable location-based Services setting from Off to On.


Enabling location based services on a Kindle Fire device

Figure 40-3


Registering an Emulator

When using an AVD Kindle Fire emulator to test maps within an application, that emulator must be registered with Amazon. An attempt to access maps on an unregistered emulator will result in the application crashing. To register an emulator, start it running and display the settings app (on an emulator this is displayed by clicking at the top of the device display and dragging the mouse to the bottom of the screen). Select More followed by My Account.

On the My Account screen, click on the Register button and enter the login and password details associated with your Amazon.com account. Once the information has been entered, click on Register and wait for the process to complete. The emulator should now support use of the Amazon Maps API.

Adjusting the Emulator Location Settings

When testing an application in the emulator, the location will be set using IP information from the internet connection of the computer system on which the emulator is running. Different locations can be simulated using the Debug Perspective within Eclipse. This can be displayed by selecting the Window -> Show Perspective -> DDMS option. When the DDMS perspective appears, select the Emulator Control tab in the main panel. At the bottom of the panel is a section named Location Controls where new Longitude and Latitude values may be entered.

Having covered the steps involved in enabling maps support in Kindle Fire applications, the remainder of this chapter will provide an overview of how map functionality may be implemented within an application.

Checking for Map Support

All Kindle Fire devices with the exception of the first generation Kindle Fire support the Amazon maps runtime library. This means that any application that intends to use the Maps API must check whether the device on which it is running supports the maps feature before attempting to make any Maps API calls. The recommended way to perform this task is to check for the presence or otherwise of the maps runtime. The following method can be included in applications and subsequently called to check whether maps are supported:

public boolean hasMapSupport()
{
	boolean result = false;
	try {
		Class.forName( "com.amazon.geo.maps.MapView" );
		result = true ;
	}
	catch (Exception e) {} 
	return result;
}

When called, the method will return a true value if maps are supported on the device and false if not.

Understanding Geocoding and Reverse Geocoding

It is impossible to talk about maps and geographical locations without first covering the subject of Geocoding. Geocoding can best be described as the process of converting a textual based geographical location (such as a street address) into geographical coordinates expressed in terms of longitude and latitude.

Geocoding can be achieved using the Android Geocoder class. An instance of the Geocoder class can, for example, be passed a string representing a location such as a city name, street address or airport code. The Geocoder will attempt to find a match for the location and return a list of Address objects that potentially match the location string, ranked in order with the closest match at position 0 in the list. A variety of information can then be extracted from the Address objects, including the longitude and latitude of the potential matches.

The following code, for example, requests the location of the National Air and Space Museum in Washington, D.C.:

double latitude;
double longitude;

List<Address> geocodeMatches = null;

geocodeMatches = new Geocoder(this).getFromLocationName("600 Independence Ave SW, Washington, DC 20560", 1);

if (!geocodeMatches.isEmpty())
{
	latitude = geocodeMatches.get(0).getLatitude(); 
	longitude = geocodeMatches.get(0).getLongitude();
}

Note that the value of 1 is passed through as the second argument to the getFromLocationName() method. This simply tells the Geocoder to return only one result in the array. Given the specific nature of the address provided, there should only be one potential match. For more vague location names, however, it may be necessary to request more potential matches and allow the user to choose the correct one. The above code is an example of forward-geocoding in that coordinates are calculating based on a text location description. Reverse-geocoding, as the name suggests, involves the translation of geographical coordinates into a human readable address string. Consider, for example, the following code:

List<Address> geocodeMatches = null;
String Address1;
String Address2;
String State;
String Zipcode;
String Country;

geocodeMatches = 
   new Geocoder(this).getFromLocation(38.8874245, -77.0200729, 1);

if (!geocodeMatches.isEmpty())
{
	Address1 = geocodeMatches.get(0).getAddressLine(0);
	Address2 = geocodeMatches.get(0).getAddressLine(1);
	State = geocodeMatches.get(0).getAdminArea();
	Zipcode = geocodeMatches.get(0).getPostalCode();
	Country = geocodeMatches.get(0).getCountryName();
}

In this case the Geocoder object is initialized with latitude and longitude values via the getFromLocation() method. Once again, only a single matching result is requested. The text based address information is then extracted from the resulting Address object. It should be noted that the geocoding is not actually performed on the Kindle Fire device, but rather on a server to which the device connects when a translation is required and the results subsequently returned when the translation is complete. As such, geocoding can only take place when the Kindle Fire has an active internet connection.

Adding a MapView to an Application

The simplest way to add a MapView to the application is to specify it in the user interface layout XML file for an activity. The following example layout file shows a MapView instance added as the child of a RelativeLayout view:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MapViewActivity" >
   
   <com.amazon.geo.maps.MapView
       android:id="@+id/mapview"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:clickable="true"
       android:enabled="true" />
</RelativeLayout>

Next, the activity with which the layout file is associated must be derived from the MapActivity class, instead of the Activity class. Failure to follow this rule will result in the application crashing when the map is invoked. The following code, for example, shows the activity implementation for the above layout:

public class MapViewActivity extends MapActivity {

	private static MapView mapView;
	
	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map_view);
        mapView = (MapView) findViewById(R.id.mapview);
     }
.
.
}

When executed, the above code would create and display a map, which, by default, will show the entire world. The user will be able to interact with the map, panning using swipes and zooming in and out using pinch gestures. In addition, a set of built-in zoom controls can be enabled on the map view via a call to the setBuiltInZoomControls() method of the MapView object:

mapView.setBuiltInZoomControls(true);

Once enabled, the controls will appear for a brief period of time when the map first appears. Subsequent taps on the map view will re-display the controls for a limited time before they again recede from view.

Customizing a Map View using the MapController

Each MapView instance has associated with it a MapController object. A reference to this controller can be obtained via a call to the getController() method of the corresponding MapView instance. Once this reference has been obtained, a variety of methods may be called on the controller to perform tasks such as setting the zoom level and center point of the map, animating zoom effects and scrolling by specified numbers of pixels.

The following code sets the zoom level (which must be an integer between 1 and 21 inclusive) to 18 before setting the center of the map view to a specific set of coordinates:

MapControler mapController = mapView.getController();

GeoPoint newLocation = new GeoPoint((int)(address.getLatitude() * 1E6), 
              (int)(address.getLongitude() *1E6));

mapController.setZoom(18);
mapController.setCenter(newLocation);

When setting the center of the map, the location needs to be provided in the form of a GeoPoint object which can be created by specifying the longitude and latitude as microdegrees (equivalent to degrees * 1E6).

Displaying the User’s Current Location

The user’s current location can be marked on the map view by making use of the MyLocationOverlay class. This is achieved by enabling access to the user’s current location, getting a list of the current overlays assigned to the map view, creating a new MyLocationOverlay object and adding it to the overlay list.

If the map is not currently displaying an area that includes the user’s current location, the location marker will not be visible on the map. The center of the map, however, can be changed to match the current location as demonstrated in the following code fragment:

myLocationOverlay = new MyLocationOverlay(this, mapView);
		 
mapView.getOverlays().add(myLocationOverlay);

myLocationOverlay.enableMyLocation();

GeoPoint myLocation = myLocationOverlay.getMyLocation();
         
mapController.setCenter(myLocation);
            
mapController.setZoom(18);

When executed, the map will center on the user’s current location and display a marker at that point on the map.

Creating an Itemized Overlay

The purpose of the itemized overlay is to allow multiple locations to be marked on a map view. The steps involved in working with itemized overlays will be covered in detail in the chapter entitled Marking Android Map Locations using Amazon Map Overlays, but can be summarized as follows:

1. A new class needs to be created that subclasses the ItemizedOverlay<OverlayItem> class.

2. A set of required methods must be implemented in the class created in step 1.

3. An instance of the new ItemizedOverlay subclass is created in the map activity class and initialized with the drawable image that is to be used as the location marker.

4. An OverlayItem object is created for each location on the map for which a marker is required to be displayed. Each object is initialized with the location at which the marker is to appear together with optional text that may be displayed when the location is tapped by the user.

5. Each OverlayItem object created in step 4 is added to the ItemizedOverlay instance created in step 3.

6. The ItemizedOverlay instance is added to the map view overlays.

Summary

Along with the introduction of location awareness in the more recent Kindle Fire generations came the Amazon Maps API. This API is intended to be compatible with the Google Maps API and allows mapping capabilities to be built into Kindle Fire based Android applications.

This chapter has provided an overview of the key classes that make up the Amazon Maps API and outlined the steps involved in preparing both the development environment and an application project to make use of the Amazon Maps API.


<google>BUY_KINDLE_FIRE</google>



PreviousTable of ContentsNext
Kindle Fire Audio Recording and Playback using MediaPlayer and MediaRecorderA Kindle Fire Amazon Maps API Tutorial