Wednesday, 24 April 2013

Google Map API V2. (part 2 continued from part 1)


Part 1 displayed map on the screen. In part 2 i will show how to add markers and poly line to map. The map should display marker's on the source and destination co-ordinates along with the route between fro source to destination.

The Map Object

The Google Maps Android API allows you to display a Google map in your Android application. These maps have the same appearance as the maps you see in the Google Maps for Mobile (GMM) app, and the API exposes many of the same features.
Define a private GoogleMap variable.
private GoogleMap mMap;
In your activity check map is null. 
         if (mMap == null) 
          {
              mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map) .getMap();
       if (mMap != null) {
           // The Map is verified. It is now safe to manipulate the map. 
                        }
         }
Override onStart() of your activity. To get the current location latitude and longitude you need GPS Enabled. So check if it is enabled or not. If not enables prompt the user to enable the same.
        @Override
protected void onStart()
       {
       super.onStart();
       LocationManager locationManager =
               (LocationManager) getSystemService(Context.LOCATION_SERVICE);
       final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
       if (!gpsEnabled) {
        gpsAlert(); 
       }
   }
   public void gpsAlert(){
    AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("Enable GPS");
alertDialogBuilder
.setMessage("Yes")
.setCancelable(false)
.setPositiveButton("Yes",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
enableLocationSettings();
dialog.cancel();
}
 })
.setNegativeButton("No",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
dialog.cancel();
}
});
AlertDialog alertDialog = alertDialogBuilder.create(); 
alertDialog.show();
}
   private void enableLocationSettings() {
       Intent settingsIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
       startActivity(settingsIntent);
   }
Get the current latitude and longitude of your current location.
       double clati,clongi;
LocationManager locationmanage = (LocationManager) getSystemService(Context.LOCATION_SERVICE);   
 Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
  if (location != null) {
 clati = location.getLatitude();
  clongi = location.getLongitude();
 }


Use the current latitude and longitude and zoom the map to the co-ordinates using the Camera
      LatiLng  source = new LatLng(clati,clongi);
      CameraUpdate update= CameraUpdateFactory.newLatLngZoom(source,9);
       mMap.animateCamera(update);
Add a marker to the current latitude and longitude
      Marker source = mMap.addMarker(new MarkerOptions()
                    .position(sc)
                    .title("MyHome")
                    .snippet("Bangalore")
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.pin)));
Use the destination latitude and longitude and place a marker to the co-ordinates on the map.
     double dlati,dlongi;
     dlati=12.900250;
     dlongi=77.594622;
      LatiLng  dest = new LatLng(dlati,dlongi);
       Marker destination= mMap.addMarker(new MarkerOptions()
                    .position(dest)
                    .title("MapleBear Head Office")
                    .snippet("Jayanager")
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.pin)));
Next we can display the route using between the source and the destination. For that we need all latitude and longitude co-ordinates between source and destination. Using the source and destination latitude and longitude we will make a http get request to the google server to get the list of latitudes and longitudes.
Making network related operation should be done on background thread on Android 3.0 and above. We will use asynctask for this purpose
 Load the Asynctask on the UI Thread
             new connectAsyncTask().execute()
   private class connectAsyncTask extends AsyncTask<Void, Void, Void>{
       private ProgressDialog progressDialog;
       @Override
       protected void onPreExecute() {
           // TODO Auto-generated method stub
           super.onPreExecute();
           progressDialog = new ProgressDialog(MainActivity.this);
           progressDialog.setMessage("Fetching route, Please wait...");
           progressDialog.setIndeterminate(true);
           progressDialog.show();
       }
       @Override
       protected Void doInBackground(Void... params) {
           // TODO Auto-generated method stub
           fetchData();
           return null;
       }
       @Override
       protected void onPostExecute(Void result) {
 // Parse the xml response and use the polyline to display the route between source and destination
           super.onPostExecute(result);           
           if(doc!=null){
               NodeList _nodelist = doc.getElementsByTagName("status");
               Node node1 = _nodelist.item(0);
               String _status1  = node1.getChildNodes().item(0).getNodeValue();
               if(_status1.equalsIgnoreCase("OK")){
                Toast.makeText(MainActivity.this,"OK" , 1000).show();
                   NodeList _nodelist_path = doc.getElementsByTagName("overview_polyline");
                   Node node_path = _nodelist_path.item(0);
                   Element _status_path = (Element)node_path;
                   NodeList _nodelist_destination_path = _status_path.getElementsByTagName("points");
                   Node _nodelist_dest = _nodelist_destination_path.item(0);
                   String _path  = _nodelist_dest.getChildNodes().item(0).getNodeValue();
                   List<LatLng> points = decodePoly(_path);
                   for (int i = 0; i < points.size() - 1; i++) {
                     LatLng src = points.get(i);
                     LatLng dest = points.get(i + 1);
                        // Polyline to display the routes
                     Polyline line = mMap.addPolyline(new PolylineOptions()
                             .add(new LatLng(src.latitude, src.longitude),
                               new LatLng(dest.latitude,dest.longitude))
                             .width(2).color(Color.BLUE).geodesic(true))   
                 }
                   progressDialog.dismiss();
               }else{
                                   // Unable to find route
                      }
           }else{
                            // Unable to find route
           }
       }
   }
The below function will return a list of latitude and longitudes between source and destination
   private List<LatLng> decodePoly(String encoded) {    
            List<LatLng> poly = new ArrayList<LatLng>();
            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 p = new LatLng((((double) lat / 1E5)), (((double) lng / 1E5)));
                poly.add(p);
            }
            return poly;
        }


Fetch the response which is in XML format.
 Document doc = null;
   private void fetchData()
   {
       StringBuilder urlString = new StringBuilder();
       urlString.append("http://maps.google.com/maps/api/directions/xml?origin=");
       urlString.append( Double.toString(flati));
       urlString.append(",");
       urlString.append( Double.toString(flongi));
       urlString.append("&destination=");//to
       urlString.append( Double.toString(dlati));
       urlString.append(",");
       urlString.append( Double.toString(dlongi));
       urlString.append("&sensor=true&mode=walking");    
       Log.d("url","::"+urlString.toString());
       HttpURLConnection urlConnection= null;
       URL url = null;
       try
       {
           url = new URL(urlString.toString());
           urlConnection=(HttpURLConnection)url.openConnection();
           urlConnection.setRequestMethod("GET");
           urlConnection.setDoOutput(true);
           urlConnection.setDoInput(true);
           urlConnection.connect();
           DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
           DocumentBuilder db = dbf.newDocumentBuilder();
           doc = (Document) db.parse(urlConnection.getInputStream());//Util.XMLfromString(response);
       }catch (MalformedURLException e){
           e.printStackTrace();
       }catch (IOException e){
           e.printStackTrace();
       }catch (ParserConfigurationException e){
           e.printStackTrace();
       }
       catch (SAXException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
   }
When you run the project on your device you can see marker and route between source and destination.
You can also specify few settings on how the map is displayed
            mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); // Satellite map
            UiSettings uis =mMap.getUiSettings();
    ui.setCompassEnabled(true);    //enable compass



Using Spannable String in Android



SpannableString can be used to style texts in textview. You can style individual words in textview.

                            
                            TextView tv = (TextView) findViewById(R.id.textView); 
                            String myString = "Hello World";

                            SpannableString  ss=  new SpannableString(myString);
                            ss.setSpan(new StyleSpan(Typeface.BOLD), 0, ss.length(), 0);
                            //make the spannable string bold.
                            //first parameter is to style the text
                            //second parameter is 0 indicating the index from which the text should be styled.
                            //third parameter is the legth of the spannable string.                           
            ss.setSpan(new StyleSpan(Typeface.ITALIC), 0, ss.length(), 0);
                            //make the spannable string italic
            ss.setSpan(new ForegroundColorSpan(Color.BLUE), 0, ss.length(), 0);   
                            //change the color of the spannable string     
            tv.setText(ss);

Make Spannable String Clickable

      ss.setSpan(MyClickableSpan(ss) , 0, ss.length(), 0);

      class MyClickableSpan extends ClickableSpan
      {

                    String clicked;
                    //Constructor to receive the clicked text
                    public MyClickableSpan(String string) {
            super();
            clicked =string;
            }
                     
                    // action to be performed on click 
                    public void onClick(View tv) {
                    Toast.makeText(MainActivity.this,clicked ,Toast.LENGTH_SHORT).show();   
                    }

                    //update the drawable state of the text  
                    public void updateDrawState(TextPaint ds) {
                    ds.setColor(Color.BLUE);
                    //set text color 
                    ds.setStrokeWidth(15f);
                    //set stroke width   
                    ds.setUnderlineText(true);
                    // set to false to remove underline
                     }
     } 
   
          
Google map api v2 on Android.(part 1)

Steps to get map working on Android.

1.  Download the  Google Play services.
         Goto Windows. Goto Android Sdk Manager. Choose Google play services under extras.
         If not installed install the package.





2.  Copy the google-play services_lib library project to your workspace. The library project can be found under the following path.

         <android-sdk-folder>/extras/google/google_play_services/libproject/google-play-services_lib library project .

3. Import the library project to your eclipse

    Click File > Import, select Android > Existing Android Code into Workspace, and browse the     workspace  import the library project. You can check if it is library project. Right click on the library project. Goto properties. Click Android on the left panel. You will see Is Library checked.

4. Create your Android project like normally you would do for other projects.

5. You need to refer the google-play services_lib library project in your android project.

Right click on your android project.  Goto properties. Choose Android on the left panel. Click on Add and browse the library project. Select the same. Click ok and apply.

6. Obtain API key
   
     A) Locate your debug keystore file. The file name is debug.keystore, and is created the first time you build your project. By default, it is stored in the same directory as your Android Virtual Device (AVD) files:
  • OS X and Linux~/.android/
  • Windows Vista and Windows 7C:\Users\your_user_name\.android\
If you are using Eclipse with ADT, and you're not sure where your debug keystore is located, you can select Windows > Prefs > Android > Build to check the full path, which you can then paste into a file explorer to locate the directory containing the keystore.
    B) If your using linux or mac os, open terminal and use the following 
      keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
Example for linux:
      keytool -list -v -keystore /home/raghu/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android  

       If you are using Windows or Vista use the following in our command prompt
       keytool -list -v -keystore "C:\Users\your_username\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android.

Note: keytool can be found under jdk bin folder. jdk1.7.0_07/bin/keytool
When you execute the above commands you should see the below output
  Alias name: androiddebugkey
 Creation date: Jan 01, 2013
 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: 4aa9b300
 Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01 18:04:04 PST 2033
 Certificate fingerprints:
      MD5:  AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
      SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75
      Signature algorithm name: SHA1withRSA
      Version: 3

The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the sequence of 20 two-digit hexadecimal numbers.
                           SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75.
7 .Open the browser and open the following url. (Rememeber to login into google account)
                        https://code.google.com/apis/console
   You should see a window as below
     

   Click on the create project. On the left you can see API Project. You can click the drop down list and rename it to whatever you like. Click on the services. Scroll Down the list and look for Google Maps API V2 and Google Maps Android ApI v2. Enable the same.
Click on API Access on the left panel. Click on create new Android key

Add the fingerprint along with your android project package name.
    BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.example.android.mapexample 
Note: You package name is seperated by a semicolon. Click On Create.

 You should the above window. Copy the API key for later use.

Adding the API Key to your application

The final step is to add the API key to your application. It goes in your application's manifest, contained in the file AndroidManifest.xml. From there, the Maps API reads the key value and passes it to the Google Maps server, which then confirms that you have access to Google Maps data.
To add the key to your application:
  1. In AndroidManifest.xml, add the following element as a child of the <application> element, by inserting it just before the closing tag</application>:
    <meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value="your_api_key"/>
    
    substituting your API key for your_api_key. This element sets the key com.google.android.maps.v2.API_KEY to the value your_api_key and makes the API key visible to any MapFragment in your application.
  2. Add the following elements to your manifest. Replace com.example.mapdemo with the package name of your application
    <permission
            android:name="com.example.mapdemo.permission.MAPS_RECEIVE"
            android:protectionLevel="signature"/>
    <uses-permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/>


         3. Add the following permissions in the Manifest file
<uses-permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/>  
 <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
     <!-- The following two permissions are not required to use
     Google Maps Android API v2, but are recommended. -->
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/> 
In the above permission you need to replace com.example.mapdemo with your project package name.

Because version 2 of the Google Maps Android API requires OpenGL ES version 2, you must add a <uses-feature> element as a child of the <manifest>element in AndroidManifest.xml:
<uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>


Save the manifest file.
The last few steps
Add the following to your activity_main.xml file
     <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"
    android:gravity="center"
    tools:context=".MainActivity" >
      <TextView
          android:id="@+id/textView1"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignParentTop="true"
          android:layout_centerHorizontal="true"
          android:text="My Map" />
         <fragment 
          android:id="@+id/map"
          android:layout_below="@+id/textView1"
          android:layout_width="match_parent"
          android:layout_height="fill_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>
</RelativeLayout>
MainActivity

package com.example.mapdemo;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}
Run the project on your device and you should see the Google map on your screen