Hello Guys. In this post, we will learn how to make a Google Maps App that detects our current location. Here Google Map V2 will track current location of user and add a marker on it.
Pre-requisites
There are some pre-requisites that you should have before going through this post:
1) Android Studio installed on your PC (Unix or Windows).
2) A real time android device (Smartphone or Tablet) configured with Android Studio.
3) A basic knowledge of Android lifecycle and different classes & functions used in Android Studio.
It could be somewhat complex for the first timers but believe us, you will find Google Maps a lot easier at the end of this post.
Difference b/w Location APIs and Google Play Services:
There could be two ways to build this App. First is to use location APIs which are available since the inception of Android. But you could find it somewhat difficult to build a Google Maps App using these APIs. Second is Google Play Services (https://developers.google.com/android/guides/overview#the_google_play_services_apk) which is a bundle of APIs that makes a user’s work a lot easier.
Google Play services give us the freedom to use the newest APIs for popular Google services without worrying about device support.
Creating new project
Please follow following steps:
- Open Android Studio and make a new project with name “Google Maps App” and company domain application.example.com (I used my company domain i.e androidtutorialpoint.com. Similarly you can use yours).
- Click Next and choose android version Lollipop. Again Click Next and Choose Google Maps Activity (as shown in following pic).
- Leave all things remaining same and Click Finish.
Now you will be able to see three files:
- google_maps_api.xml (…/GoogleMapsApp/app/src/debug/res/values/google_maps_api.xml)
- MapsActivity.java (…/GoogleMapsApp/app/src/main/java/com/androidtutorialpoint/googlemapsapp/MapsActivity.java)
- AndroidManifest.xml ( …/GoogleMapsApp/app/src/main/AndroidManifest.xml)
Configuring Google Play Services
Open google_maps_api.xml. Here you will find a lot of information along with a link. Copy-Paste this link in your web browser. Also, make a Gmail account through which you will configure google play services.
Now at the browser choose Create New Project and Click Continue. Following screen will be displayed:
Click on Go to credentials. Following screen will appear.
Create your key by clicking Create. Now a key will be created that you shall copy and paste in google_maps_api.xml. Copy paste it in place where YOUR_KEY_HERE is written:
Code inside google_maps_api.xml is complete.
<resources>
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">LVwrKoLOEMgwUBXGiut0bkFhoAjOiaVemoMlymg</string>
</resources>
Code Inside AndroidManifest.xml
If you go inside AndroidManifest.xml then this key will be displayed in meta tags. Here you need to add permissions for accessing location of device. The required permission should be as follows:
ACCESS_NETWORK_STATE – To check network state i.e if we are connected to any network or not.
INTERNET – If we are connected to Internet or not.
ACCESS_COARSE_LOCATION – To determine user’s location using WiFi and mobile. It will gove us approximate location.
ACCESS_FINE_LOCATION – To determine user’s location using GPS. It will give us precise location.
OpenGL ES V2 – Required for Google Maps V2
Finally our code inside AndroidManifest.xml will be as follows:
AndroidManifest.xml
="1.0"="utf-8"
<manifest package="com.androidtutorialpoint.googlemapsapp"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<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>
MapsActivity.java
package com.androidtutorialpoint.googlemapsapp;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
@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);
}
/**
* 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;
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
In the above code Coordinated of Sydney has been added by default in onMapReady(). So when we run this App for the first time, marker will be placed at Sydney.
Running Google Maps App for the first time:
So as you know we need a real time android device to run Android App. If you are getting any problem in running Google Maps App on an android device then we would suggest you to have a look at Connect and Configure Android App. .
Now click on Run. You will see following image on Android Device:
So our basic Google Maps App is running fine with a marker placed at a particular location.
Note: Please see your build.gradle file. It should have following code:
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
compile ‘com.android.support:appcompat-v7:21.0.3’
compile ‘com.google.android.gms:play-services:6.5.87’
}
Third line com.google.android.gms:play-services:6.5.87
is responsible for inserting Google Play Services. Please make sure this line is present in build.gradle
Now let’s make a Google Maps App that place a marker at current user location.
Inserting callbacks:
We need to use following callback in implements portion:
GoogleApiClient.ConnectionCallbacks
: This callback will have a public function onConnected()
which will be called whenever device is connected and disconnected. GoogleApiClient.OnConnectionFailedListener
: Provides callbacks for scenarios that result in a failed attempt to connect the client to the service. Whenever connection is failed onConnectionFailed()
will be called. LocationListener
: This callback will be called whenever there is change in location of device. Function onLocationChanged()
will be called.
Now as soon as you add above callback certain errors will appear as follows:
- On seeing following error as could be seen in image Click Alt+ENTER. It will automatically import required files.
- Now as you could see callbacks are underlined with red lines as shown below:
- To this error, go to the callback and Click Alt+ENTER. Select Implement Method.
Click Ok. Now you see error will not be there and new functions will get added in MainActivity.java (onConnected()
, onConnectionSuspended()
and onLocationChanged()
)
onMapReady() Functionality
This function is called when map is ready to be used. Here we can add all markers, listeners and other functional attributes. Add following code inside onMapReady()
onMapReady()
mMap = googleMap;
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
//Initialize Google Play Services
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
}
else {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
In the above code we are setting map type as Hybrid. (For more info regarding map type you can refer Map Types ). Now we need to intialize Google Play Services. We will do it using builder method (buildGoogleApiClient()
).
After this setMyLocationEnabled is used to enable location layer which will allow a user to interact with current user location.
Define a new function protected synchronized void buildGoogleApiClient()
add following code:
buildGoogleApiClient():
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
Declare handle of GoogleApiClient
at top of class. In the above code builder method is used to build GoogleApiClient.
GoogleApiClient.Builder
is used to configure client. .addConnectionCallbacks
provides callbacks that are called when client connected or disconnected. .addOnConnectionFailedListener
covers scenarios of failed attempt of connect client to service. .addApi
adds the LocationServices API endpoint from Google Play Services. mGoogleApiClient.connect()
: A client must be connected before excecuting any operation.
Fused Location Provider:
In a Google Maps App, it is always required to update current location of user at regular intervals. Also we may also want current velocity, altitude etc. These all are covered inside location object which can be retrieved using fused location provider. Fused Location Provider analyses GPS, Cellular and Wi-Fi network location data in order to provide the highest accuracy data. It uses different device sensors to define if a user is walking, riding a bicycle, driving a car or just standing in order to adjust the frequency of location updates. You can read more about location updates here. So it helps in android location tracking. We will use this to get the last updated location. Add following code inside onConnected()
:
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
In the above code mLocationRequest
is used to get quality of service for location updates from the FusedLocationProviderApi
using requestLocationUpdates
.
Note: You have to declare mLocationRequest at the start of class. LocationRequest mLocationRequest;
So till now we have connected our Google Maps App with APIs and able to get regular location intervals. The next step is to what to do whenever user location change. For that Google has predefined function onLocationChanged that will be called as soon as user location change.
Define onLocationChanged()
and add following code in it:
onLocationChanged()
@Override
public void onLocationChanged(Location location)
{
mLastLocation = location;
if (mCurrLocationMarker != null) {
mCurrLocationMarker.remove();
}
//Place current location marker
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(latLng);
markerOptions.title("Current Position");
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
mCurrLocationMarker = mMap.addMarker(markerOptions);
//move map camera
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
//stop location updates
if (mGoogleApiClient != null) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
}
In the above code we are using certain objects that you have to first declare at the start of class:
Location mLastLocation;
Marker mCurrLocationMarker;
Here we are simply getting coordinates of current location using getLatitude()
and getLongitude()
and then positioning marker on it.
So finally our Google Maps App is almost ready. The only thing remaining is getting the location permissions.
Requesting Location Permission
In Android 6.0 Marshmallow, application will not be granted any permission at installation time. Instead, application has to ask user for a permission one-by-one at runtime. So now add following code:
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission(){
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Asking user if explanation is needed
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
In the above code checkSelfPermission
returns PackageManager.PERMISSION_GRANTED
, and the app can proceed with the operation. If the app does not have the permission, the method returns PERMISSION_DENIED
, and the app has to explicitly ask the user for permission. Now if user permission is not granted then App will proceed for showing explanation to user. shouldShowRequestPermissionRationale
method returns true if the app has requested this permission previously and the user denied the request. It will return false if user has chosen Don’t ask again option when it previously asked for permission.
Finally, the last is step is remaining i.e showing permission results.
Handling permission request response
A dialog box is presented whenever any App requests permissions. When the user responds, the system invokes app’s onRequestPermissionsResult()
method, passing it the user response. Our app has to override this method to find out whether the permission was granted. Add below code.
onRequestPermissionsResult():
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
mMap.setMyLocationEnabled(true);
}
} else {
// Permission denied, Disable the functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
}
return;
}
// other 'case' lines to check for other permissions this app might request.
//You can add here other case statements according to your requirement.
}
}
Above code is pretty much self explanatory. We have added comments wherever required.
You can see full code in following files:
MapsActivity.java
package com.androidtutorialpoint.googlemapsapp;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
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.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private GoogleMap mMap;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
Marker mCurrLocationMarker;
LocationRequest mLocationRequest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkLocationPermission();
}
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
}
else {
buildGoogleApiClient();
mMap.setMyLocationEnabled(true);
}
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
@Override
public void onConnected(Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onLocationChanged(Location location) {
mLastLocation = location;
if (mCurrLocationMarker != null) {
mCurrLocationMarker.remove();
}
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(latLng);
markerOptions.title("Current Position");
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
mCurrLocationMarker = mMap.addMarker(markerOptions);
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
if (mGoogleApiClient != null) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission(){
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
mMap.setMyLocationEnabled(true);
}
} else {
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
}
return;
}
}
}
}
google_maps_api.xml
<resources>
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">LVwrKoLOEMgwUBXGiut0bkFhoAjOiaVemoMlymg</string>
</resources>
AndroidManifest.xml
="1.0"="utf-8"
<manifest package="com.androidtutorialpoint.googlemapsapp"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<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>
So finally our app is complete. We would suggest you to turn on GPS and Internet Connection. Run this Google Maps App on any real android device. It will display your location.