May 14, 2015

Google Place API with Autocomplete in Xamarin Android

In this post i will write down how to make use of the google map place api in xamarin android. Before proceeding here go through with Google map integration have written in steps about google map integration.

In Brief: As and when user enter the character, fetch the matching location with that character from the google server.


In Detail:
Step 1)Register in google developer console.
1.1) Login to http://console.developers.google.com
In "APIs & auth" menu select APIs option. From Google Maps APIs enable "Google Places API Web Service"



Enable that API






1.2)Again from "APIs & auth" select "Credential". From "Public API access" select "Create new key"

->Browser key. Leave referer space blank, select "Create".


This generates API key. copy this key which is to be used for the communication from the app.


Step 2.) Create a new project with AutocompleteTextview in layout.
Now on textchange of Autocompletetextview make a webservice request to google server along with input charecter and api key.


https://maps.googleapis.com/maps/api/place/autocomplete/json?input=character&key=API_KEY
here i took only two query string options input and key. you can find out more option from the Google developer place api document.


-> Download the JSON string from the webservice[How to do REST request and JSON response processing].
-> Parse downloaded JSON string to Class "GoogleMapPlaceClass"
you will get the number of online tool to get class from json.
-> Bind the "Prediction" class attribute "description" to Adapter of Autocomplete textview.
This is what i done in  the below code,

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Gms.Maps;
using Android.Gms.Maps.Model; 
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net;
using Android.Views.InputMethods;
using System.Text;

//enter you google map key in manifestfile

namespace GoogleMapPlaceAPI
{
 [Activity ( Label = "GoogleMapPlaceAPI" , MainLauncher = true , Icon = "@drawable/icon" )]

 public class MainActivity : Activity
 { 
  const string  strAutoCompleteGoogleApi="https://maps.googleapis.com/maps/api/place/autocomplete/json?input=";
  //browser key for place webservice
  const string strGoogleApiKey="*****yCaRfEwrLeUvdADPn_R9WQ_WrP3jB*****";
  const string strGeoCodingUrl="https://maps.googleapis.com/maps/api/geocode/json";
  AutoCompleteTextView txtSearch;
  MapFragment mapFrag;
  GoogleMap map;
  ArrayAdapter adapter=null;  
  GoogleMapPlaceClass objMapClass;
  GeoCodeJSONClass objGeoCodeJSONClass;
  string autoCompleteOptions;
  string[] strPredictiveText;
  int index = 0;
 
 protected override void OnCreate (Bundle savedInstanceState)
  {
  base.OnCreate (savedInstanceState); 
  SetContentView ( Resource.Layout.Main );
//Each Tag <,> enclosed with single quote to avoid HTML interpretation,Pl aviod it
  txtSearch = FindViewById'<'autocompletetextview'>'( Resource.Id.txtTextSearch );  
  txtSearch.ItemClick += AutoCompleteOption_Click;
  txtSearch.Hint = "Enter source  "; 
  mapFrag = (MapFragment) FragmentManager.FindFragmentById(Resource.Id.map);
  map = mapFrag.Map; 
  map.MapType = GoogleMap.MapTypeNormal; 
  map.MyLocationEnabled = true;

  txtSearch.TextChanged += async  delegate(object sender, Android.Text.TextChangedEventArgs e)
   {  
  try
  {
 autoCompleteOptions=await fnDownloadString(strAutoCompleteGoogleApi+txtSearch.Text+"&key="+strGoogleApiKey);

  if ( autoCompleteOptions == "Exception" )
  {
 Toast.MakeText ( this , "Unable to connect to server!!!" , ToastLength.Short ).Show ();
   return;
  } 
    objMapClass = JsonConvert.DeserializeObject'<'googlemapplaceclass'>' (autoCompleteOptions);
    strPredictiveText= new string[objMapClass.predictions.Count];
    index = 0;
    foreach(Prediction objPred  in objMapClass.predictions)
   {
   strPredictiveText[index] = objPred.description;
   index++; 
   } 
   adapter = new ArrayAdapter'<'string'>' ( this ,Android.Resource.Layout.SimpleDropDownItem1Line, strPredictiveText ); 
 txtSearch.Adapter = adapter; 
     }
 catch
     {
Toast.MakeText ( this , "Unable to process at this moment!!!" , ToastLength.Short ).Show ();
     }

     };  
  
 }
async void  AutoCompleteOption_Click(object sender,AdapterView.ItemClickEventArgs e)
 { 
   //to soft keyboard hide
   InputMethodManager inputManager = (InputMethodManager)this.GetSystemService (Context.InputMethodService);
   inputManager.HideSoftInputFromWindow (txtSearch.WindowToken, HideSoftInputFlags.NotAlways);
   map.Clear ();
   if(txtSearch.Text!=string.Empty)
   { 
    var sb = new StringBuilder();
    sb.Append(strGeoCodingUrl);
    sb.Append("?address=").Append(txtSearch.Text);
    string strResult=await fnDownloadString(sb.ToString()); 
    if ( strResult == "Exception" )
    {
     Toast.MakeText ( this , "Unable to connect to server!!!" , ToastLength.Short ).Show ();
      
    }
//below used single quote to avoid html interpretation
    objGeoCodeJSONClass= JsonConvert.DeserializeObject'<'geocodejsonclass'>' (strResult);  
    LatLng Position=new LatLng(objGeoCodeJSONClass.results[0].geometry.location.lat,objGeoCodeJSONClass.results[0].geometry.location.lng); 
    updateCameraPosition (Position);
    MarkOnMap("MyLocation",Position); 
   }

  } 
  void MarkOnMap(string title,LatLng pos )
  { 
   RunOnUiThread ( () =>
   {
    var marker = new MarkerOptions ();
    marker.SetTitle ( title ); 
    marker.SetPosition ( pos );
    map.AddMarker ( marker ); 
   } );

  } 
  void updateCameraPosition(LatLng pos)
  {
   CameraPosition.Builder builder = CameraPosition.InvokeBuilder(); 
   builder.Target(pos);
   builder.Zoom(14);
   builder.Bearing(45);
   builder.Tilt(90);
   CameraPosition cameraPosition = builder.Build(); 
   CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition); 
   map.AnimateCamera(cameraUpdate);
  }
  async Task fnDownloadString(string strUri)
  { 
   WebClient webclient = new WebClient ();
   string strResultData;
   try
   {
    strResultData= await webclient.DownloadStringTaskAsync (new Uri(strUri));
    Console.WriteLine(strResultData);
   }
   catch
   {
    strResultData = "Exception";
    RunOnUiThread ( () =>
    { 
     Toast.MakeText ( this , "Unable to connect to server!!!" , ToastLength.Short ).Show ();
    } );
   }
   finally
   {
    webclient.Dispose ();
    webclient = null; 
   }

   return strResultData;
  }
   
 }
}

Layout file

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_height="fill_parent"
 android:layout_width="fill_parent"
 android:orientation="vertical">
    <autocompletetextview 
     android:hint="Enter Location" 
     android:id="@+id/txtTextSearch" 
     android:imeactionid="@+id/imeisearch"
     android:imeoptions="actionSearch" 
     android:inputtype="text" 
     android:layout_height="wrap_content" 
     android:layout_marginleft="5dp" 
     android:layout_marginright="5dp" 
     android:layout_width="match_parent"/>
    <fragment android:id="@+id/map"
     android:layout_below="@+id/first" 
     android:layout_height="match_parent" 
     android:layout_width="match_parent" 
     class="com.google.android.gms.maps.MapFragment"/>
</linearlayout>


On selection of autocompletetextview option i have marked that location in google map. [How to mark on the google map].

On autocompletetextview ItemClick event is used to display the map.

Result Screen:
This is useful whenever we need to enter the location within the application. Avoids the wrong entry by autocomplete option and one can present the global location by using this google place api.
Thank you :)
Download source code