Android Google Map Version 2 using Google Play Services

In this tutorial, we are going to learn how to implement android Google Map version 2 using Google Play services.

There are situations where you will like to implement a map in your application, for example to tag a particular address in a map and it trace a route using a map and make more.

For this android tutorial, we will add a map in our android app. Then we will move ahead to implement GoogleMap.OnMapLongClickListener   and  GoogleMap.OnMapClickListener callback methods.

We will add an address marker whenever a user clicks in any part of the map. The code to implement this will be called in the onMapClick(LatLng latLng).

For the onMapLongClick(LatLng latLng), we are going to draw a circle with a certain radius around a particular location in a map.

If you are interested in getting the last known address of a user only, then you should consider using
Android Location Service API using Google Play Services. I wrote a tutorial on this and I will suggest you first read this post first.

Android Google Play Services helps you take advantage of the latest, Google-powered features such as Maps, Google+, and more, with automatic platform updates distributed as an APK through the Google Play store. This makes it faster for your users to receive updates and easier for you to integrate the newest that Google has to offer.

If you want to read more about Google Play Service, you can refer to the link in android developers guide.

Before we go deeper into this tutorial, it is important for us to understand what we are planning to achieve. Below is the screen-shot of the application we will be creating.

mapexample

Lets start to soil our hands in code. we will create our project in our IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 7

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: MapsActivity

Package: com.inducesmile.mapsactivity

Select Blank Activity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

Now that we have created our android project, we are going to add Google Play Services as one of our application dependency module.

Open your android build.gradle file located in gradle script/build.gradle , copy and paste this line of code to the dependencies section.

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit: junit: 4.12 "
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.google.android.gms:play-services:8.4.0'
}

Go to the res folder, then double click on the values folder and double click on the colors.xml file and add the following lines of codes. This will holder all the colors we will use in this tutorial.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

Now, head over to the strings.xml file and modify the content with the code below.

<resources>
<string name="app_name">MapsActivity</string>
<string name="title_activity_maps">Android Map</string>
</resources>

Since we are going to use Google Map API, we will use permission to access the location in the map. The user must grant the request before the app can access the user location.

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

Open you Mainfest.xml , copy and paste the following code inside the file. The complete code for the Mainfest.xml file will look like this.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.inducesmile.mapsactivity">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<activity
android:name=".MapsActivity"
android:label="@string/title_activity_maps">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

The next thing we will do is to obtain Google Maps API key for our application. There are different ways we can achieve this. I will suggest you read more about Google Maps API key in the documentation.

In our case, we will follow the instruction presented in the google_maps_api.xml file as shown in the file below.

<resources>
<!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=57:A8:DD:42:FF:9E:88:E6:EC:69:71:B7:0D:2C:5A:31:B4:7F:F9:46%3Bcom.inducesmile.mapsactivity
You can also add your credentials to an existing key, using this line:
57:A8:DD:42:FF:9E:88:E6:EC:69:71:B7:0
Alternatively, follow the directions here:
https://developers.google.com/maps/documentation/android/start#get-key
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
-->
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">Google Maps API key</string>
</resources>

Copy the commented line that looks like this – https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=57:A8:DD:42:FF:9E:88:E6:EC:69:71:B7:0D:2C:5A:31:B4:7F:F9:46%3Byourpackagename.applicationname and paste it to your browser. Click enter to load the page. You will see the following page display.

google map api

Select Create a new project and click on the Continue button. It will take some time before the API is enabled

google maps api

Click on the Go to credentials button to obtain the Credentials as shown

google map api

Click on the create button to finish off. Copy your Google Map API key value and paste the value into this line of code.

<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">Google Map API key</string>

The complete code for the google_maps_api.xml file is shown below.

<resources>
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">Google Maps API Key</string>
</resources>

Now, open the activity_maps.xml file, we are going to add a Fragment Map in the layout that will hold the Map object. The code snippet for this class is shown below.

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

In the MapsActivity file, we will add this line of code.

private final int[] TYPESOfMAPS = {GoogleMap.MAP_TYPE_SATELLITE, GoogleMap.MAP_TYPE_NORMAL, GoogleMap.MAP_TYPE_HYBRID, GoogleMap.MAP_TYPE_TERRAIN, GoogleMap.MAP_TYPE_NONE};

There is an int-array of the different map types you can create. An examples of the maps are shown below. (Google Map)

android map

Create a Map object by using the findViewById(int r) method of the Activity class. We are going to implement the following interfaces. Add the following line in the class declaration.

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleMap.OnMapLongClickListener, GoogleMap.OnMapClickListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

We add the GoogleAPiClient as a class variable

 private GoogleApiClient mGoogleApiClient;

Instanciate the GoogleAPiClient in the onCreate() method of the Activity class.

if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}

In the onStart() method, connect the GoogleApiClient instance to Google Play Services as shown below.

@Override
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}

In onStop() method, the GoogleApiClient is disconnected in order to free resources when the activity is finally destroyed.

@Override
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}

We will override the onMapReady(GoogleMap googleMap), in this method we will get the instance of that map object which is ready to be used. The map type will be set and the click and longClick events are set to the map object.

public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(TYPESOfMAPS[1]);
mMap.setOnMapLongClickListener(MapsActivity.this);
mMap.setOnMapClickListener(MapsActivity.this);
}

In the onConnected(Bundle bundle) callback method which signified that the GoogleApiClient instance is connected, LocationRequest is send to see if there is any settings that is not enable that the Location service Api might need.

The setResultCallback() method of the result instance checks if all the setting required is enable, if so the last known location is requested from from the FusedLocationApi class .

Please not that starting from Android API level 23, that permissions are requested at run-time so we have to always request user permission. The down side of it is that if a user refuse to grant a permission that our application will use, we will find a way to handle it so that it will not crash our application or make it behave in a way that was not intended.

We will pass to instantiate of the returned location object to the LatLng constructor then add the location marker to the location point.

The complete code on this method is shown below.

@Override
public void onConnected(Bundle bundle) {
mLocationRequest = createLocationRequest();
builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
@Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates mState = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can
// initialize location requests here.
if (ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MapsActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
} else {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mMap != null) {
LatLng locationMarker = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
mMap.addMarker(new MarkerOptions().position(locationMarker).title("Current Location"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(locationMarker));
}
}
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(MapsActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way
// to fix the settings so we won't show the dialog.
break;
}
}
});

In the onMapClick(LatLng latLng), we will add marker in any click in the map.

@Override
public void onMapClick(LatLng latLng) {
MarkerOptions options = new MarkerOptions().position( latLng );
options.title("New Location");
options.icon(BitmapDescriptorFactory.defaultMarker());
if(mMap != null){
mMap.addMarker( options );
}
}

In the onMapLongClick(LatLng latLng), we are going to call displayCircleOnMap(latLng) method inside the event method. The displayCircleOnMap(LatLng) definition is as shown below.

private void displayCircleOnMap(LatLng mLatLng){
CircleOptions circleOptions = new CircleOptions();
circleOptions.center(mLatLng);
circleOptions.radius(1000);
circleOptions.fillColor(Color.BLUE);
circleOptions.strokeColor(Color.RED);
circleOptions.strokeWidth(10);
if(mMap != null){
mMap.addCircle(circleOptions);
}
}

The complete code for the MainActivity class is shown below.

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStates;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleMap.OnMapLongClickListener, GoogleMap.OnMapClickListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private GoogleMap mMap;
private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;
private final int REQUEST_LOCATION = 200;
private final int REQUEST_CHECK_SETTINGS = 300;
private final int REQUEST_GOOGLE_PLAY_SERVICE = 400;
private Circle circle;
private LocationRequest mLocationRequest;
private PendingResult<LocationSettingsResult> result;
private LocationSettingsRequest.Builder builder;
private final int[] TYPESOfMAPS = { GoogleMap.MAP_TYPE_SATELLITE, GoogleMap.MAP_TYPE_NORMAL, GoogleMap.MAP_TYPE_HYBRID, GoogleMap.MAP_TYPE_TERRAIN, GoogleMap.MAP_TYPE_NONE };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
@Override
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
@Override
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
@Override
protected void onResume() {
super.onResume();
int resultReturned = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getApplicationContext());
if(resultReturned != ConnectionResult.SUCCESS){
GoogleApiAvailability.getInstance().getErrorDialog(MapsActivity.this, ConnectionResult.SERVICE_MISSING, REQUEST_GOOGLE_PLAY_SERVICE);
}
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(TYPESOfMAPS[1]);
mMap.setOnMapLongClickListener(MapsActivity.this);
mMap.setOnMapClickListener(MapsActivity.this);
}
@Override
public void onConnected(Bundle bundle) {
mLocationRequest = createLocationRequest();
builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
@Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates mState = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can
// initialize location requests here.
if (ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MapsActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
} else {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mMap != null) {
LatLng locationMarker = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
mMap.addMarker(new MarkerOptions().position(locationMarker).title("Current Location"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(locationMarker));
}
}
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(MapsActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way
// to fix the settings so we won't show the dialog.
break;
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CHECK_SETTINGS:
switch (resultCode) {
case Activity.RESULT_OK:
if (ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MapsActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MapsActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
} else {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mMap != null) {
LatLng locationMarker = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
mMap.addMarker(new MarkerOptions().position(locationMarker).title("Current Location"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(locationMarker));
}
}
break;
case Activity.RESULT_CANCELED:
// user does not want to update setting. Handle it in a way that it will to affect your app functionality
Toast.makeText(MapsActivity.this, "User does not update location setting", Toast.LENGTH_LONG).show();
break;
}
break;
}
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
protected LocationRequest createLocationRequest() {
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
return mLocationRequest;
}
@Override
public void onMapClick(LatLng latLng) {
MarkerOptions options = new MarkerOptions().position( latLng );
options.title("New Location");
options.icon(BitmapDescriptorFactory.defaultMarker());
if(mMap != null){
mMap.addMarker( options );
}
}
@Override
public void onMapLongClick(LatLng latLng) {
displayCircleOnMap(latLng);
}
private void displayCircleOnMap(LatLng mLatLng){
CircleOptions circleOptions = new CircleOptions();
circleOptions.center(mLatLng);
circleOptions.radius(1000);
circleOptions.fillColor(Color.BLUE);
circleOptions.strokeColor(Color.RED);
circleOptions.strokeWidth(10);
if(mMap != null){
mMap.addCircle(circleOptions);
}
}
}

Please note that I did not test this tutorial on device running android 6 but it has been tested from android 5 and below.

Run your application and see for yourself what we have just created.

This brings us to the end of this tutorial, If you find anything confusing kindly contact me with your questions or use the comment box below.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

 

Remember to subscribe with your email address so that you will be among the first to receive my new post once it is published.

Please if you love this tutorial, kindly download my new android app – Daily Calculator Tool – in Google Play Store and let me know what you think about it.

OTHER INTERESTING POSTS:

2 Comments

Add a Comment