April 24, 2015

Build Google Map V2 in Xamarin Android

Here i’m going to discuss about the steps to integrate the google map in Xamarin android. Cross platform tool Xamarin let's the C# .Net developers to develop App to run on diffent platforms like, Android,iOS and Windows. By sharing maximum amount of code.

In Brief : Integrating google map,highlighting desired location and customizing map controls.


In Detail : Step by step procedure to Integrate google map in Xamarin android.
Step 1: Ensure “Google Play service” installed by opening SDK Manager.
Open Tool -> Android SDK Manager.
If not installed,
1.a)Download and Install Google Play service from Extras section

1.b) Download and Install the Google API for API level choose for the development.
Here  selected for android 5.0 Lolipop (API level 22)

1.c) Select and Accept to install the the Google API

That’s all from IDE Configuration section

Step 2: Add google play service library to Project. 
Right click on Packages folder, select Add Packages
In package window Search for “Google Play service”.

Step 3: Generate SHA1 fingerprint of the keystore  
To complete this let us quickly go through the following sub steps

3.a) Create new keystore for a project [follow Preparing app release]
Using command line tool called KeyTool  run the command :

$ keytool -genkey -v -keystore <filename>.keystore -alias <key-name> -keyalg RSA -keysize 2048 -validity 10000
for example:
$ keytool -genkey -v -keystore sample.keystore -alias samplepublishing -keyalg RSA -keysize 2048 -validity 10000

Search and locate the keystore created.

->To work in Debug mode, make use of the Xamarin Android default Debug.Keystore found in following location.

Windows - C:\Users\[USERNAME]\AppData\Local\Xamarin\Mono for Android\debug.keystore
OSX - /Users/[USERNAME]/.local/share/Xamarin/Mono for Android/debug.keystore 
Find SHA1 code for debug.keystore by using command:

keytool -list -v -keystore ~/.local/share/Xamarin/Mono\ for\ Android/debug.keystore -alias androiddebugkey -storepass android -keypass android
3.b) Generate KeyHash SHA1 for the kestore, 
straightway run this cmd,
keytool -list -v -keystore "/Users/name/sample.keystore" -alias samplepublishing -storepass pwd -keypass pwd

Step 4: Create Project in Google developer console and Register SHA1
4.a)Navigate to Google developer console  
Create new project-> Enter project name ->Create
4.b)Select project 

4.c)Select API menu from Left pane. 
Select-> “Google Maps Android API” and Enable API


4.d)Select Credential menu from Left pane. 
Select->Create new Key


Androidkey->Paste SHA1 key(generated from step3.b) add package name separated by semi colon. Here you can enter more than one key[ex: for debug and release mode ] in the next line.
that’s it from Google developer console registration.

Step 5: Write Map initialization code
Now create new Android project and Add the activity with following code to initialize the map.
Here our app code interacts with the Google play services using the phone google play client library.

using Android.App;
using Android.OS;
using Android.Widget;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;

namespace GoogleMapApp
{
[Activity ( Label = "Google Map", MainLauncher = true , Icon = "@drawable/icon" )]
public class GoogleMapActivity : Activity
{  
MapFragment mapFrag;
GoogleMap map;
const double latitude=12.9543;
const double longitude=77.5924;
protected override void OnCreate ( Bundle bundle )
{
base.OnCreate ( bundle );
SetContentView ( Resource.Layout.GoogleMapLayout ); 
mapFrag = (MapFragment) FragmentManager.FindFragmentById(Resource.Id.map);
fnInitiateMap ();
}
void fnInitiateMap()
{
if ( map != null )
{
map.Clear ();
map.Dispose ();
}
map = mapFrag.Map;
if (map != null)
{
//To initialize the map 
map.MapType =GoogleMap.MapTypeSatellite; //select the map type
CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
builder.Target(new LatLng(latitude,longitude)); //Target to some location hardcoded
builder.Zoom(8); //Zoom multiplier
builder.Bearing(45);//bearing is the compass measurement clockwise from North
builder.Tilt(90); //tilt is the viewing angle from vertical
CameraPosition cameraPosition = builder.Build();
CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
map.AnimateCamera(cameraUpdate);
}
} 
}
}

[In the above code(line 30) "map = mapFrag.Map;" marked as deprecated,find alternate here : Android.Gms.Maps.MapFragment.Map is obsolete deprecated xamarin android ]

Create a new layout Add fragment to be inherited from Google map fragment class
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rlContainr"
    android:layout_width="match_parent"
    android:layout_height="match_parent"> 
    <fragment
        android:id="@+id/map" 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment" />
</RelativeLayout>

Step 6: Manifest requires the following permission

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.name.GoogleMapApp">
    <uses-sdk android:minSdkVersion="13" android:targetSdkVersion="21" />
    <!-- Google Maps for Android v2 requires OpenGL ES v2 -->
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <!-- We need to be able to download map tiles and access Google Play Services-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- Allow the application to access Google web-based services. -->
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <!-- Google Maps for Android v2 will cache map tiles on external storage -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- Google Maps for Android v2 needs this permission so that it may check the connection state as it must download data -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- Permission to receive remote notifications from Google Play Services -->
    <!-- Notice here that we have the package name of our application as a prefix on the permissions. -->
    <uses-permission android:name="com.name.GoogleMapApp.permission.MAPS_RECEIVE" />
    <permission android:name="com.name.GoogleMapApp.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
    <!-- These are optional, but recommended. They will allow Maps to use the My Location provider. -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <application android:label="GoogleMapApp" android:icon="@drawable/icon">
        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIz****fqjZJ8RiDa-lnkpkfU1z4V**********" />
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
    </application>
</manifest>


Step 7: Run the application to using a real device.[Google Play service client may not support in emulator]


Step 8: Change Map type: 
map.MapType = GoogleMap.MapTypeHybrid;

MapTyprNormal
MapTypeTerrain
MapTypeHybrid
Placing the marker :
   Placing the marker
          var marker = new MarkerOptions ();
   marker.SetPosition(new LatLng(latitude,longitude));
   marker.SetTitle ("Hello");
   map.AddMarker ( marker );

Change the marker icon:
  //Changing marker color
   var marker = new MarkerOptions ();
   marker.Icon = BitmapDescriptorFactory.DefaultMarker ( BitmapDescriptorFactory.HueBlue );
   marker.InvokeIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
   //load from drawable
   //marker.SetIcon ( BitmapDescriptorFactory.FromResource ( resourceId ) ); 
  marker.SetPosition(new LatLng(latitude,longitude));
  map.AddMarker ( marker );

Move camera position with animation :

  CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
  builder.Target(new LatLng(latitude,longitude)); //Target to some location hardcoded
  builder.Zoom(8); //Zoom multiplier
  builder.Bearing(45);//bearing is the compass measurement clockwise from North
  builder.Tilt(90); //tilt is the viewing angle from vertical
  CameraPosition cameraPosition = builder.Build(); 
  CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition); 
  map.AnimateCamera(cameraUpdate);

Bug and Debug: 
I spend an almost a day to resolve some of these errors, 
->App loads with white background without showing any map. 
Error related to the SHA1 code registered in Google developer console. Ensure that Package name and SHA1 code entered are valid. 
For the APK creation use the same keystore from which SHA1 code generated.

Run in release mode by setting the keystore in IDE. For that, Double tap the project->Android Package Signing Add that same keystore.

-> From Setting->Apps-Select app-> Clear Data. if you have changed the console data(Package name/SHA1) otherwise it will not reflect in the app.

->GooglePlayService component update to new version may throw some error, i suggest to follow this link http://forums.xamarin.com/discussion/comment/142485/  this helped me to solve the issue.

Final touch :
 Even though android comes with default google map app, Most of the app development requirement need this to be done natively from the app. Here i have shared my walkthrough experience with google map integration. 
Thank you for the visit, Comment/Suggest me if you have any better way to do this :).
Download Source Code

Update : As Google play service component has updated, Please note the following changes
a)Remove old component added
b)Right click on Packages -> Add Packages, search google play services
c)Select Xamarin Google play services- Base,Map,Location packages
d)Install these  three packages and delete current solution Bin and Obj folder and rebuild

Please check updated code here : https://github.com/suchithm/GoogleMapXamDroid