Archive for the ‘Guide – Google Maps for Android’ Category

Guide: Google Maps V2 for Android : Update InfoWindow with an Image Asynchronously:

One of the needs that raise up in my last project was to update an Google Maps InfoWindow
with an image that I receive asynchronously from the web. Now the problem with this is
that the the view of the InfoWindow on the map is not the actual view you created in the xml
editor but an image rendered from it. If you look at the docs you will see:

Note: The info window that is drawn is not a live view. The view is rendered as an image (using View.draw(Canvas)) at the time it is returned. This means that any subsequent changes to the view will not be reflected by the info window on the map. To update the info window later (for example, after an image has loaded), call showInfoWindow(). Furthermore, the info window will not respect any of the interactivity typical for a normal view such as touch or gesture events. However you can listen to a generic click event on the whole info window as described in the section below.

Now that we cleared that, another thing that I want to mention is that for loading the image I use
the Picasso library by Square that you can read more about in this blog post I wrote:
Guide: How to load images asynchronously into a ListView.

Next it’s important to mention that the layout file you provide for the InfoWindow must include it’s
own background, because you are not going to be provided with the default Google Maps
InfoWindow background. So here is the code for the custom InfoWindowAdapter:

private class CustomInfoWindowAdapter implements InfoWindowAdapter {

    private View view;
    LayoutInflater inflater = null;

     public CustomInfoWindowAdapter(LayoutInflater inflater) {
         //Inflating the InfoWindow view.
         this.inflater = inflater;
         view = inflater.inflate(R.layout.info_window, null);
     }

     @Override
     public View getInfoContents(final Marker marker) {
         //Re-show InfoWindow if it already shown
         if ( App.getInstance().currentlyClickedMarker != null && App.getInstance().currentlyClickedMarker.isInfoWindowShown() ) {
             App.getInstance().currentlyClickedMarker.hideInfoWindow();
             App.getInstance().currentlyClickedMarker.showInfoWindow();
         }
         return null;
     }

     @Override
     public View getInfoWindow(final Marker marker) {
         App.getInstance().currentlyClickedMarker = marker;  	    
         TextView venueName = (TextView) view.findViewById(R.id.info_window_name);
         TextView venueAddress = (TextView) view.findViewById(R.id.info_window_address);
         ImageView venueLogo = (ImageView) view.findViewById(R.id.ivLogo);
         //Get the Image from web using Picasso and update the info contents		
         Picasso.with(getActivity()).load(App.getInstance().getVenueByAddress(marker.getSnippet()).getLogo()).into(venueLogo, new Callback() {	
             @Override
             public void onSuccess() {
                 getInfoContents(marker);
             }
  				
             @Override
             public void onError() {}
         });
         venueName.setText(marker.getTitle());
         venueAddress.setText(marker.getSnippet());
         return view;
    }
}    

Finally you will have to set this adapter to the GoogleMap object:

map.setInfoWindowAdapter(new CustomInfoWindowAdapter(getActivity().getLayoutInflater()));
June 20, 2014Emil Adjiev Comments Off
FILED UNDER :Guide , Guide - Android Development , Guide - Google Maps for Android

Guide: Google Maps V2 for Android: Draw Driving Direction on Map:

This is again one of those popular questions that I stumble upon, I already gave here an
answer for this one, but I decided to put it in here anyways as a post. So let get started.
First of all if you are not familiar with Google’s new Maps API, I suggest you to first of all read
this post: Google Maps API V2 Key to get your own API key which is required to
integrate Google maps in your application. Next follow this post: Google Maps API V2
to actually integrate Google Maps in your application.

If you already have a map integrated in your application and you want to add driving navigation
to it, you came to the right place. Here I will present you the code that will allow you to paint a line
(Polyline object on your map) to show the directions from point A to B. But first I would like
to point your to this quote:

Google Maps/Google Earth APIs Terms of Service

  Last updated: May 27, 2009
...
10. License Restrictions. Except as expressly permitted under the Terms, or unless you
have received prior written authorization from Google (or, as applicable, from the
provider of particular Content), Google's licenses above are subject to your adherence
to all of the restrictions below. Except as explicitly permitted in Section 7 or the
Maps APIs Documentation, you must not (nor may you permit anyone else to):
...
10.9 use the Service or Content with any products, systems, or applications for or in
connection with:

(a) real time navigation or route guidance, including but not limited to turn-by-turn
route guidance that is synchronized to the position of a user's sensor-enabled device;

and may be disabled for certain apps (somehow, at least on Android)... FromGeocode
scraping in .NET conversation:
This is not allowed by the API terms of use. You should not scrape Google Maps to
generate geocodes. We will block services that do automated queries of our servers.

Bret Taylor
Product Manager, Google Maps

I might be mistaken but from my understanding of this quote Google does not allow to show
driving navigation using Google Maps. Now that we covered that I will show you how you can
paint on the map.

1. So lets start with the GMapV2Direction class, this class is responsible to
make a request to Google Directions API and get the navigation instruction for 2 LatLng points:

public class GMapV2Direction {
    public final static String MODE_DRIVING = "driving";
    public final static String MODE_WALKING = "walking";

    public GMapV2Direction() { }

    public Document getDocument(LatLng start, LatLng end, String mode) {
        String url = "http://maps.googleapis.com/maps/api/directions/xml?"
                + "origin=" + start.latitude + "," + start.longitude
                + "&destination=" + end.latitude + "," + end.longitude
                + "&sensor=false&units=metric&mode=" + mode;

        try {
            HttpClient httpClient = new DefaultHttpClient();
            HttpContext localContext = new BasicHttpContext();
            HttpPost httpPost = new HttpPost(url);
            HttpResponse response = httpClient.execute(httpPost, localContext);
            InputStream in = response.getEntity().getContent();
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(in);
            return doc;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getDurationText (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("duration");
        Node node1 = nl1.item(0);
        NodeList nl2 = node1.getChildNodes();
        Node node2 = nl2.item(getNodeIndex(nl2, "text"));
        Log.i("DurationText", node2.getTextContent());
        return node2.getTextContent();
    }

    public int getDurationValue (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("duration");
        Node node1 = nl1.item(0);
        NodeList nl2 = node1.getChildNodes();
        Node node2 = nl2.item(getNodeIndex(nl2, "value"));
        Log.i("DurationValue", node2.getTextContent());
        return Integer.parseInt(node2.getTextContent());
    }

    public String getDistanceText (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("distance");
        Node node1 = nl1.item(0);
        NodeList nl2 = node1.getChildNodes();
        Node node2 = nl2.item(getNodeIndex(nl2, "text"));
        Log.i("DistanceText", node2.getTextContent());
        return node2.getTextContent();
    }

    public int getDistanceValue (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("distance");
        Node node1 = nl1.item(0);
        NodeList nl2 = node1.getChildNodes();
        Node node2 = nl2.item(getNodeIndex(nl2, "value"));
        Log.i("DistanceValue", node2.getTextContent());
        return Integer.parseInt(node2.getTextContent());
    }

    public String getStartAddress (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("start_address");
        Node node1 = nl1.item(0);
        Log.i("StartAddress", node1.getTextContent());
        return node1.getTextContent();
    }

    public String getEndAddress (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("end_address");
        Node node1 = nl1.item(0);
        Log.i("StartAddress", node1.getTextContent());
        return node1.getTextContent();
    }

    public String getCopyRights (Document doc) {
        NodeList nl1 = doc.getElementsByTagName("copyrights");
        Node node1 = nl1.item(0);
        Log.i("CopyRights", node1.getTextContent());
        return node1.getTextContent();
    }

    public ArrayList getDirection (Document doc) {
        NodeList nl1, nl2, nl3;
        ArrayList listGeopoints = new ArrayList();
        nl1 = doc.getElementsByTagName("step");
        if (nl1.getLength() > 0) {
            for (int i = 0; i < nl1.getLength(); i++) {
                Node node1 = nl1.item(i);
                nl2 = node1.getChildNodes();

                Node locationNode = nl2.item(getNodeIndex(nl2, "start_location"));
                nl3 = locationNode.getChildNodes();
                Node latNode = nl3.item(getNodeIndex(nl3, "lat"));
                double lat = Double.parseDouble(latNode.getTextContent());
                Node lngNode = nl3.item(getNodeIndex(nl3, "lng"));
                double lng = Double.parseDouble(lngNode.getTextContent());
                listGeopoints.add(new LatLng(lat, lng));

                locationNode = nl2.item(getNodeIndex(nl2, "polyline"));
                nl3 = locationNode.getChildNodes();
                latNode = nl3.item(getNodeIndex(nl3, "points"));
                ArrayList arr = decodePoly(latNode.getTextContent());
                for(int j = 0 ; j < arr.size() ; j++) {
                    listGeopoints.add(new LatLng(arr.get(j).latitude, arr.get(j).longitude));
                }

                locationNode = nl2.item(getNodeIndex(nl2, "end_location"));
                nl3 = locationNode.getChildNodes();
                latNode = nl3.item(getNodeIndex(nl3, "lat"));
                lat = Double.parseDouble(latNode.getTextContent());
                lngNode = nl3.item(getNodeIndex(nl3, "lng"));
                lng = Double.parseDouble(lngNode.getTextContent());
                listGeopoints.add(new LatLng(lat, lng));
            }
        }

        return listGeopoints;
    }

    private int getNodeIndex(NodeList nl, String nodename) {
        for(int i = 0 ; i < nl.getLength() ; i++) {
            if(nl.item(i).getNodeName().equals(nodename))
                return i;
        }
        return -1;
    }

    private ArrayList decodePoly(String encoded) {
        ArrayList poly = new ArrayList();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;
        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;                 shift += 5;             } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;
            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;                 shift += 5;             } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng position = new LatLng((double) lat / 1E5, (double) lng / 1E5);
            poly.add(position);
        }
        return poly;
    }
}

2. Because finding the desired route is a potentially long running task,
we need to place this in a AsyncTask in order to avoid blocking the UI-Thread.
So we are going to create the following GetDirectionsAsyncTask:

public class GetDirectionsAsyncTask extends AsyncTask<Map<String, String>, Object, ArrayList>
{
    public static final String USER_CURRENT_LAT = "user_current_lat";
    public static final String USER_CURRENT_LONG = "user_current_long";
    public static final String DESTINATION_LAT = "destination_lat";
    public static final String DESTINATION_LONG = "destination_long";
    public static final String DIRECTIONS_MODE = "directions_mode";
    private MapFragmentActivity activity;
    private Exception exception;
    private ProgressDialog progressDialog;

    public GetDirectionsAsyncTask(MapFragmentActivity activity)
    {
        super();
        this.activity = activity;
    }

    public void onPreExecute()
    {
        progressDialog = new ProgressDialog(activity);
        progressDialog.setMessage("Calculating directions");
        progressDialog.show();
    }

    @Override
    public void onPostExecute(ArrayList result)
    {
        progressDialog.dismiss();
        if (exception == null)
        {
            activity.handleGetDirectionsResult(result);
        }
        else
        {
            processException();
        }
    }

    @Override
    protected ArrayList doInBackground(Map<String, String>... params)
    {
        Map<String, String> paramMap = params[0];
        try
        {
            LatLng fromPosition = new LatLng(Double.valueOf(paramMap.get(USER_CURRENT_LAT)) , Double.valueOf(paramMap.get(USER_CURRENT_LONG)));
            LatLng toPosition = new LatLng(Double.valueOf(paramMap.get(DESTINATION_LAT)) , Double.valueOf(paramMap.get(DESTINATION_LONG)));
            GMapV2Direction md = new GMapV2Direction();
            Document doc = md.getDocument(fromPosition, toPosition, paramMap.get(DIRECTIONS_MODE));
            ArrayList directionPoints = md.getDirection(doc);
            return directionPoints;
        }
        catch (Exception e)
        {
            exception = e;
            return null;
        }
    }

    private void processException()
    {
        Toast.makeText(activity, activity.getString(R.string.error_when_retrieving_data), 3000).show();
    }
}

3. No we would like to use this AsyncTask to find the road direction from one
location to another. To make the usage of this AsyncTask a little bit more convenient I have
created the following findDirections method in my MapFragmentActivity:

public void findDirections(double fromPositionDoubleLat, double fromPositionDoubleLong, double toPositionDoubleLat, double toPositionDoubleLong, String mode)
{
    Map<String, String> map = new HashMap<String, String>();
    map.put(GetDirectionsAsyncTask.USER_CURRENT_LAT, String.valueOf(fromPositionDoubleLat));
    map.put(GetDirectionsAsyncTask.USER_CURRENT_LONG, String.valueOf(fromPositionDoubleLong));
    map.put(GetDirectionsAsyncTask.DESTINATION_LAT, String.valueOf(toPositionDoubleLat));
    map.put(GetDirectionsAsyncTask.DESTINATION_LONG, String.valueOf(toPositionDoubleLong));
    map.put(GetDirectionsAsyncTask.DIRECTIONS_MODE, mode);

    GetDirectionsAsyncTask asyncTask = new GetDirectionsAsyncTask(this);
    asyncTask.execute(map);
}

What I’m doing here is creating a HashMap with the needed coordinates
(Start Lat and Long, End Lat and Long), Passing them to the AsyncTask and executing it.

4. Next, we will create the following handleGetDirectionsResult method,
to handle the AsyncTask result and to actually paint the directions Polyline on the map.

public void handleGetDirectionsResult(ArrayList directionPoints)
{
    Polyline newPolyline;
    GoogleMap mMap = ((SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
    PolylineOptions rectLine = new PolylineOptions().width(3).color(Color.BLUE);
    for(int i = 0 ; i < directionPoints.size() ; i++)
    {
        rectLine.add(directionPoints.get(i));
    }
    newPolyline = mMap.addPolyline(rectLine);
}

5. Finally to get the direction we need to run the findDirections method
with the desired locations in code like this:

findDirections(SGTasksListAppObj.getInstance().currentUserLocation.getLatitude(),
SGTasksListAppObj.getInstance().currentUserLocation.getLongitude(),
clickMarkerLatLng.latitude, clickMarkerLatLng.longitude, GMapV2Direction.MODE_DRIVING );

The final result should look something like that:

direction_blue

As you see a blue directions line is drown on the map, for showing the way you need to
go for your destination. And that’s it, you are finished.

6. UPDATE: Due to many requests I have created a small Android project that
demonstrates the usage of this code. You can download this project from here.
The project show how to navigate from Amsterdam to Paris or to Frankfurt, just
remember to add your Google Maps API V2 Key in the manifest file.

Enjoy and stay tuned.

Guide: Google Maps V2 for Android : Creating your Google Map Application:

If you already got your Google Maps Android API V2 key then you are ready to create
your map application. If you don’t head to my Guide: Google Maps V2 for Android :
Getting the API key
post to read how to get it.

So lets start:
1. Open Eclipse and create a new Android project.
The first thing that we will handle is the import of Google Map classes.
To get the Google Maps files we need to download the last version of
Google Play Services via the Android SDK Manager.

2. After you downloaded the Google Play Services, restart Eclipse and
in the Package Explorer Right-Click –> Import….
In the opened windows choose “Existing Android Code into Workspace” and click “Next”.
Click the “Browse…” Button and head to the location of your SDK folder.
in it find the following folder:

\extras\google\google_play_services\libproject\google-play-services_lib

and press “OK”, check the V next to it in the window and press the “Finish” button.

3. Now you added Google Play Services to your work space, we have to create a reference
from our project to this library. Right-Click your project and choose “Properties” go
to the Android section, in the lower part press the “Add…” button and add a reference
to that library. Your result should be as in the screen shot bellow:

adding google play services library

Note: If you try to reference google-play-service library and you receive a red X next to this
reference, what you should do is to move the .jar file to a location where it’s path will be shorter
and then reference it again.

4. Another import we have to make in order to make our application work on
Operation system prior to API11 is to import the support library this can be done very easily
using Eclipse: Right-Click you project and choose “Android Tools” and then choose
“Add Support Library…”:

supportlibrary

When you finish those import you should have the following libraries (Red) in the
Android Dependencies folder (Green) of your project:
Untitled

5. We are now ready for some codding: First of all open the Android Manifest file:
add the following permissions:

<permission android:name="your.application.package.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
<uses-permission android:name="your.application.package.permission.MAPS_RECEIVE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Important: Replace your application package instead of the current “your.application.package” string.

As mentioned in the comments by @Keilaron it looks like since the Google Play Services
3.1.59 update the MAPS_RECEIVE permissions are completely unnecessary and as a result
they can be removed.

6. Next, Google Maps uses OpenGL so we have to add OpenGL support to our application
by adding this to the Manifest file:

<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>

7. Finally add your key to you application right before you close your “application” node
in the Manifest file:

<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="Your Google Maps API V2 Key" />

8. Now create an Activity that extends from “FragmentActivity”:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MapActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.map_layout);
    }
}

9. Finally for map_layout, XML layout file that was set
as a content view of the map activity write the following:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <fragment
        android:name="com.google.android.gms.maps.SupportMapFragment"
        xmlns:map="http://schemas.android.com/apk/res-auto"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

10. UPDATE: The last update for Google Play Services library revision 13 introduced
a new meta-data tag that should be added as well to your Android Manifest file.
So go ahead and add this right next to your API key meta-data code:

<meta-data 
android:name="com.google.android.gms.version" 
android:value="@integer/google_play_services_version" />

And that’s it, run the application and you should see a full screen map:

map

Remember that if you want to run the application in the emulator you should install
Google Play Services first.

Enjoy and stay tuned.

Guide: Google Maps V2 for Android : Getting the API key

Since I have been working on an Android application that uses Google map
services I have decided to write a couple of posts on this topic.
The application will be posted here as well in the near future.

The first thing you have to know when implementing Google Maps for android is
that you have to produce your own Google Map Android API V2 key.
Now there is a reason that I’m marking this name, take a careful look that you turning on
the right Key in you API Console and that you have chosen the API for Android
and not the Google Maps API V2 or V3 for example.

So how do we get an API key, you ask? Well it’s a few steps task:
1. First of all we need to find our SHA1 key. For debugging
purposes we will use the debug.keystore file to get our SHA1 key. to do that
we will use a tool named ‘Keytool‘ that come with Java installation.
so open you command prompt, head to the following location:

C:\Program Files\Java\<your JDK or JRE instllation>\bin>

and run the next command:

keytool -list -v -keystore C:\Users\<your user name>\.android\debug.keystore -storepass android -keypass android

You will recive the following output:

SHA1 key pic
Copy the SHA1 key by right-clicking the command prompt choosing ‘Mark’, then
mark the all key and right-click again on the marked text to copy it.

2. Now that we have the SHA1 key we can head to the Google API’s Console,
sign-in with our Google account and create a new project by clicking the drop box
in the upper-left corner of the screen and choosing the ‘Create…’ option:

create in google api

Give a name to your project and create it.

3. Once you have done that head to the Services section and turn on the Google Map Android API V2:

api service

4. Now open the API Access section and press the Create new Android key… button.

In the window that opens you will have to enter you SHA1 key you got in the 1. section adding
to it your application package name after a semicolon as in the example:

45:B5:E4:6F:36:AD:0A:98:94:B4:02:66:2B:12:17:F2:56:26:A0:E0;com.example.your_project_name

After pressing the Create button you will recieve you own Google Maps API V2 key for Android.

My next post will explain how to use this key to create a Google map in your application.