Show Current Location On Map Kotlin Android Marker Example

best android development course on udemy, google map in fragment kotlin, show current location on map kotlin, Kotlin Volley JSON Parsing, Kotlin Detect Shake, kotlin google map

How to Show Current Location On Map Kotlin Android Marker Example.

You will learn to make google map marker on the current location using Kotlin.

Any feature like whatsapp’s live location need to show current location on the google map.

This feature is little bit complex to implement.

This tutorial will help you to show current location using latitude and longitude on google map in kotlin.

First of all, show the below output video to see this feature.

Generating Google API

To integrate google map in the android app, we need to have one API key.

I have written separate tutorial on how to get Google map API key.

Read this example to generate Google API key.

After reading above tutorial, you will have one Google API key with you.

We need to write this key in our new android studio project.

Separate Project in Android Studio

Make a new fresh new project in the android studio.

While making new project, make sure you have selected “Empty activity” as the default one.

If you select “Map Activity” then compiler write some lines of code which is difficult to understand.

Adding Gradle Lines

In your project, open build.gradle(Module:app) file.

Add the following lines in the build.gradle(Module:app) file.

 implementation 'com.google.android.gms:play-services:12.0.1'
    implementation 'com.karumi:dexter:5.0.0'

First line will integrate google services like push notification, map etc.

Second line will help us to integrate Dexter library in our project.

Manifest File

Open your AndroidManifest.xml file. Add the following source code in <application> tag.

<meta-data
      android:name="com.google.android.geo.API_KEY"
      android:value="AI.....FQ"/>

An attribute android:value=”” holds the Google API key as it’s value.

You need to write your Google API key in it.

Now outside of <application> tag and inside of <manifest> tag, add the following

 <uses-feature
            android:glEsVersion="0x00020000"
            android:required="true"/>

You also need to add the location permission. So add the below line

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

So final source snippet for the AndroidManifest.xml file is as the below

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.googmapcurrlocakotlin">

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

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">

        <meta-data
                android:name="com.google.android.geo.API_KEY"
                android:value="AIzaSyAILP1ad7_3k576cUEUI5a06HpQHXLQcFQ"/>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

    <uses-feature
            android:glEsVersion="0x00020000"
            android:required="true"/>

</manifest>

Main Files Changes

There should be two main files in your project : android_main.xml and MainActivity.kt

In your android_main.xml file, write down the below source lines,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        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:orientation="vertical"
        android:layout_height="match_parent"
        android:layout_width="match_parent">

    <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

        <TextView
                android:id="@+id/latitude"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentTop="true"
                android:text="Latitude:"
                android:textSize="18sp" />
        <TextView
                android:id="@+id/latitude_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/latitude"
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@+id/latitude"
                android:textSize="16sp" />
        <TextView
                android:id="@+id/longitude"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentTop="true"
                android:text="Longitude:"
                android:layout_marginTop="24dp"
                android:textSize="18sp" />
        <TextView
                android:id="@+id/longitude_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/longitude"
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@+id/longitude"
                android:textSize="16sp"/>

    </RelativeLayout>

    <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MapsActivity" />


</LinearLayout>

Above XML layout file has four text views and one fragment tag.

<fragment> tag will work as the container for our google map.

We will load the google map inside this <fragment> tag.

Among the four text views, two are static and two will change their value from Kotlin file.

Inside MainActivity.kt file, write down the below source code snippet.

import android.Manifest
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AlertDialog
import android.util.Log
import android.widget.TextView
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.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.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.karumi.dexter.Dexter
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.single.PermissionListener

class MainActivity : AppCompatActivity() , GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener,
    OnMapReadyCallback {

    private var mMap: GoogleMap? = null
    private val TAG = "MainActivity"
    private var mLatitudeTextView: TextView? = null
    private var mLongitudeTextView: TextView? = null
    private var mGoogleApiClient: GoogleApiClient? = null
    private var mLocation: Location? = null
    private var mLocationManager: LocationManager? = null
    private var mLocationRequest: LocationRequest? = null
    private val listener: com.google.android.gms.location.LocationListener? = null
    private val UPDATE_INTERVAL = (2 * 1000).toLong()  /* 10 secs */
    private val FASTEST_INTERVAL: Long = 20000 /* 20 sec */

    private var locationManager: LocationManager? = null
    private var latLng: LatLng? = null
    private var isPermission: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mGoogleApiClient = GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()

        mLocationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager

        mLatitudeTextView = findViewById<TextView>(R.id.latitude_textview)
        mLongitudeTextView = findViewById<TextView>(R.id.longitude_textview)

        if (requestSinglePermission()) {
            // Obtain the SupportMapFragment and get notified when the map is ready to be used.
            //it was pre written
            val mapFragment = supportFragmentManager
                .findFragmentById(R.id.map) as SupportMapFragment?
            mapFragment!!.getMapAsync(this)

            mGoogleApiClient = GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build()

            mLocationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager

            checkLocation() //check whether location service is enable or not in your  phone
        }

    }


    /**
     * 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.
     */
    //it was pre written
    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap

        if (latLng != null) {
            mMap!!.addMarker(MarkerOptions().position(latLng!!).title("Marker in Current Location"))
            mMap!!.moveCamera(CameraUpdateFactory.newLatLng(latLng))
        }
    }

    override fun onConnected(bundle: Bundle?) {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return
        }

        startLocationUpdates()

        mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient)

        if (mLocation == null) {
            startLocationUpdates()
        }
        if (mLocation != null) {

            // mLatitudeTextView.setText(String.valueOf(mLocation.getLatitude()));
            //mLongitudeTextView.setText(String.valueOf(mLocation.getLongitude()));
        } else {
            Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onConnectionSuspended(i: Int) {
        Log.i(TAG, "Connection Suspended")
        mGoogleApiClient!!.connect()
    }

    override fun onConnectionFailed(connectionResult: ConnectionResult) {
        Log.i(TAG, "Connection failed. Error: " + connectionResult.errorCode)
    }

    override fun onLocationChanged(location: Location) {
        val msg = "Updated Location: " +
                java.lang.Double.toString(location.latitude) + "," +
                java.lang.Double.toString(location.longitude)
        mLatitudeTextView!!.setText(location.latitude.toString())
        mLongitudeTextView!!.setText(location.longitude.toString())
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
        // You can now create a LatLng Object for use with maps
        latLng = LatLng(location.latitude, location.longitude)

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        //it was pre written
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment!!.getMapAsync(this)
    }

    protected fun startLocationUpdates() {
        // Create the location request
        mLocationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(UPDATE_INTERVAL)
            .setFastestInterval(FASTEST_INTERVAL)
        // Request location updates
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(
            mGoogleApiClient,
            mLocationRequest, this
        )
        Log.d("reque", "--->>>>")
    }

    override fun onStart() {
        super.onStart()
        if (mGoogleApiClient != null) {
            mGoogleApiClient!!.connect()
        }
    }

    override fun onStop() {
        super.onStop()
        if (mGoogleApiClient!!.isConnected()) {
            mGoogleApiClient!!.disconnect()
        }
    }

    private fun checkLocation(): Boolean {
        if (!isLocationEnabled())
            showAlert()
        return isLocationEnabled()
    }

    private fun showAlert() {
        val dialog = AlertDialog.Builder(this)
        dialog.setTitle("Enable Location")
            .setMessage("Your Locations Settings is set to 'Off'.\nPlease Enable Location to " + "use this app")
            .setPositiveButton("Location Settings") { paramDialogInterface, paramInt ->
                val myIntent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                startActivity(myIntent)
            }
            .setNegativeButton("Cancel") { paramDialogInterface, paramInt -> }
        dialog.show()
    }

    private fun isLocationEnabled(): Boolean {
        locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return locationManager!!.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager!!.isProviderEnabled(
            LocationManager.NETWORK_PROVIDER
        )
    }

    private fun requestSinglePermission(): Boolean {

        Dexter.withActivity(this)
            .withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
            .withListener(object : PermissionListener {
                override fun onPermissionGranted(response: PermissionGrantedResponse) {
                    //Single Permission is granted
                    Toast.makeText(this@MainActivity, "Single permission is granted!", Toast.LENGTH_SHORT).show()
                    isPermission = true
                    startLocationUpdates()

                }

                override fun onPermissionDenied(response: PermissionDeniedResponse) {
                    // check for permanent denial of permission
                    if (response.isPermanentlyDenied) {
                        isPermission = false
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permission: PermissionRequest, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).check()

        return isPermission

    }
}

Main Lines in Details

Let us see how main kotlin file is making google map with current location.

First of all, read the following source code lines

private var mMap: GoogleMap? = null
    private val TAG = "MainActivity"
    private var mLatitudeTextView: TextView? = null
    private var mLongitudeTextView: TextView? = null
    private var mGoogleApiClient: GoogleApiClient? = null
    private var mLocation: Location? = null
    private var mLocationManager: LocationManager? = null
    private var mLocationRequest: LocationRequest? = null
    private val listener: com.google.android.gms.location.LocationListener? = null
    private val UPDATE_INTERVAL = (2 * 1000).toLong()  /* 10 secs */
    private val FASTEST_INTERVAL: Long = 20000 /* 20 sec */

    private var locationManager: LocationManager? = null
    private var latLng: LatLng? = null
    private var isPermission: Boolean = false

Above lines are giving us the objects for various classes like GoogleMap, GoogleApiClient, LocationManager, LatLng etc.

There are some variables also like UPDATE_INTERVAL, FASTEST_INTERVAL etc.

Now see the below coding lines

 mGoogleApiClient = GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()

        mLocationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager

First five lines are initializing the Google Api Client.

Then sixth line is initializing the LocationMAnager object.

Now go on with the following code lines

  val mapFragment = supportFragmentManager
                .findFragmentById(R.id.map) as SupportMapFragment?
            mapFragment!!.getMapAsync(this)

Compiler will first find the container of the map using findFragmentById() method.

Then it will initiate the google map.

Now below is the code for onMapReady() method.

 override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap

        if (latLng != null) {
            mMap!!.addMarker(MarkerOptions().position(latLng!!).title("Marker in Current Location"))
            mMap!!.moveCamera(CameraUpdateFactory.newLatLng(latLng))
        }
    }

When the compiler is ready to show the map with the marker on the current location, it will load the above method.

For this, we need current latitude and longitude which we will get from below method.

  override fun onLocationChanged(location: Location) {
        val msg = "Updated Location: " +
                java.lang.Double.toString(location.latitude) + "," +
                java.lang.Double.toString(location.longitude)
        mLatitudeTextView!!.setText(location.latitude.toString())
        mLongitudeTextView!!.setText(location.longitude.toString())
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
        // You can now create a LatLng Object for use with maps
        latLng = LatLng(location.latitude, location.longitude)

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        //it was pre written
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment!!.getMapAsync(this)
    }

Compiler will continuously check for the current location of the android device.

Whenever it detects any changes in the latitude or longitude or both, it will trigger the above method.

Now go through the following lines of source code

 private fun checkLocation(): Boolean {
        if (!isLocationEnabled())
            showAlert()
        return isLocationEnabled()
    }

To fetch the current location, we need to turn on the location service of the android device.

Above method will whether the location of the android device is turn on or off.

If it is off then it will ask the user to turn it on.

Now we need to ask for the runtime permission to the user.

For this, we will use the following method,

 private fun requestSinglePermission(): Boolean {

        Dexter.withActivity(this)
            .withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
            .withListener(object : PermissionListener {
                override fun onPermissionGranted(response: PermissionGrantedResponse) {
                    //Single Permission is granted
                    Toast.makeText(this@MainActivity, "Single permission is granted!", Toast.LENGTH_SHORT).show()
                    isPermission = true
                    startLocationUpdates()

                }

                override fun onPermissionDenied(response: PermissionDeniedResponse) {
                    // check for permanent denial of permission
                    if (response.isPermanentlyDenied) {
                        isPermission = false
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permission: PermissionRequest, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).check()

        return isPermission

    }

Above method is using the Dexter library. This library simplify the process of runtime permissions.

Here, we are asking for just one permission : ACCESS_FINE_LOCATION

You can also add more in the above method if you need any.

Github To Download Code

Github Source Code