In this tutorial, we’re building a car location tracking app like Careem and Uber. Although we’re not developing complete apps like Uber and Careem. But we’ll see how to online a driver and show the online driver in the passenger app and update the driver whenever its current location changed.

For online and offline the driver we’re going to use the Firebase Real-time Database. The driver simply sends its current location to the firebase and passenger see the driver on the Google Map.

Below is the demo of applications which we gonna make in this article.


  1. You’ve to have a Google Map API key for showing the Map. See this link for getting the API key.
  2. Need a Firebase project for the real-time database. Click to this link for creating a Firebase project.

Now we’ve Google Map API key and a Firebase project let’s dive into coding and start building our mobile applications.

Android App Coding:

Like I said earlier, there are two apps one for driver and one for the passenger. So, we’re gonna start making with the driver app.


First, add the below dependencies to your app level build.gradle file.

implementation ''
implementation ''
implementation ''

After adding the dependencies you need to sync or build your project. Later the successful build adds the Internet and Location permission to Android. Manifest file.

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

Now, to show the Google Maps in the app we need to add the meta tags in Android. Manifest file.

    android:value="@integer/google_play_services_version" />

    android:value="@string/map_api_key" />   // Change it with your Google Maps API key.

All the prep work are done for showing the Google Maps and for reading the user current Location. Now, we need to add the google-services.json file. In this tutorial, I’m not gonna show you how to create a firebase project, register your Android app to that firebase project, and add the google-services.json file in Android app. There’s a link for a video to add the google-services.json file in Android app.

So, without further ado let’s dive into coding. Below is the UI of the driver app that we’re gonna make.

Driver Tracker App Main Screen

You see the UI is very basic, we’ve one SwitchCompat for online and offline the driver and below is the Google Map itself. Now let’s see the code of the above UI. Below is the activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=""


            android:textSize="22sp" />

            android:theme="@style/SCBSwitch" />


        tools:context="" />



class MainActivity : AppCompatActivity() {

    companion object {
        private const val MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 2200

    private lateinit var googleMap: GoogleMap
    private lateinit var locationProviderClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private lateinit var locationCallback: LocationCallback
    private var locationFlag = true
    private var driverOnlineFlag = false
    private var currentPositionMarker: Marker? = null
    private val googleMapHelper = GoogleMapHelper()
    private val firebaseHelper = FirebaseHelper("0000")
    private val markerAnimationHelper = MarkerAnimationHelper()
    private val uiHelper = UiHelper()

    override fun onCreate(savedInstanceState: Bundle?) {
        val mapFragment: SupportMapFragment = supportFragmentManager.findFragmentById( as SupportMapFragment
        mapFragment.getMapAsync { googleMap = it }
        locationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        locationRequest = uiHelper.getLocationRequest()
        if (!uiHelper.isPlayServicesAvailable(this)) {
            Toast.makeText(this, "Play Services did not installed!", Toast.LENGTH_SHORT).show()
        } else requestLocationUpdate()
        val driverStatusTextView = findViewById<TextView>(
        findViewById<SwitchCompat>( { _, b ->
            driverOnlineFlag = b
            if (driverOnlineFlag) driverStatusTextView.text = resources.getString(R.string.online_driver)
            else {
                driverStatusTextView.text = resources.getString(R.string.offline)

    private fun requestLocationUpdate() {
        if (!uiHelper.isHaveLocationPermission(this)) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
        if (uiHelper.isLocationProviderEnabled(this))
            uiHelper.showPositiveDialogWithListener(this, resources.getString(R.string.need_location), resources.getString(R.string.location_content), object : IPositiveNegativeListener {
                override fun onPositive() {
            }, "Turn On", false)
        locationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())

    private fun createLocationCallback() {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                if (locationResult!!.lastLocation == null) return
                val latLng = LatLng(locationResult.lastLocation.latitude, locationResult.lastLocation.longitude)
                Log.e("Location", latLng.latitude.toString() + " , " + latLng.longitude)
                if (locationFlag) {
                    locationFlag = false
                if (driverOnlineFlag) firebaseHelper.updateDriver(Driver(lat = latLng.latitude, lng = latLng.longitude))

    private fun showOrAnimateMarker(latLng: LatLng) {
        if (currentPositionMarker == null)
            currentPositionMarker = googleMap.addMarker(googleMapHelper.getDriverMarkerOptions(latLng))
        else markerAnimationHelper.animateMarkerToGB(currentPositionMarker!!, latLng, LatLngInterpolator.Spherical())

    private fun animateCamera(latLng: LatLng) {
        val cameraUpdate = googleMapHelper.buildCameraUpdate(latLng)
        googleMap.animateCamera(cameraUpdate, 10, null)

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            val value = grantResults[0]
            if (value == PERMISSION_DENIED) {
                Toast.makeText(this, "Location Permission denied", Toast.LENGTH_SHORT).show()
            } else if (value == PERMISSION_GRANTED) requestLocationUpdate()

In MainActivity there are a bunch of important points which I’m going to explain below.

  1.  createLocationCallback: We’re calling this function from the onCreate method of our MainActivity. In the LocationCallback abstract method, we’ll get the user current location, update the Driver info on Firebase Real-time database if the driver is online, and animate driver car Marker from the previous Location to a new one.
  2. requestLocationUpdates: Calling this function from the onCreate method of MainActivity if the user has installed GooglePlayServices in his/her mobile. In this method first, we request the Location permission from the user, then we check if the Location provider is enabled, and finally start the location updates.
  3. showOrAnimateMarker: In this method first we check if the driver car Marker is null then we simply add a new Marker to Google Maps else we animate the Marker.
  4. animteCamera: This method is called from createLocationCallback method only once because, we only want to animate the Google Maps to driver current location when he/she opens the app.
  5. onRequestPermissionsResult: Callback of the result of requesting location permission. This method invoked when the user performs an action on permission. Now in this method, if the user denies the permission then we simply show the toast else start the current location updates.


class UiHelper {

    fun isPlayServicesAvailable(context: Context): Boolean {
        val googleApiAvailability = GoogleApiAvailability.getInstance()
        val status = googleApiAvailability.isGooglePlayServicesAvailable(context)
        return ConnectionResult.SUCCESS == status

    fun isHaveLocationPermission(context: Context): Boolean {
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED

    fun isLocationProviderEnabled(context: Context): Boolean {
        val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)

    fun showPositiveDialogWithListener(callingClassContext: Context, title: String, content: String, positiveNegativeListener: IPositiveNegativeListener, positiveText: String, cancelable: Boolean) {
        buildDialog(callingClassContext, title, content)
                .positiveColor(getColor(R.color.colorPrimary, callingClassContext))
                .onPositive { _, _ -> positiveNegativeListener.onPositive() }

    private fun buildDialog(callingClassContext: Context, title: String, content: String): MaterialDialog {
        return MaterialDialog.Builder(callingClassContext)

    private fun getColor(color: Int, context: Context): Int {
        return ContextCompat.getColor(context, color)

    fun getLocationRequest() : LocationRequest {
        val locationRequest = LocationRequest.create()
        locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        locationRequest.interval = 3000
        return locationRequest

The following explains UiHelper class methods.

  1. isPlayServicesAvailable: This method checks that if the user currently has Google Play Services installed or not.
  2. isHaveLocationPermission: Checks that if a user gives the Location permission or not.
  3. isLocationProviderEnabled: Checks if the Location provider enabled or not. If not then open the Settings activity and turn on the Location Provider.
  4. showPositiveDialogWithListener: Utility function to show the Dialog when the user has not enabled the location provider in mobile settings.


class GoogleMapHelper {

    companion object {
        private const val ZOOM_LEVEL = 18
        private const val TILT_LEVEL = 25

     * @param latLng in which position to Zoom the camera.
     * @return the [CameraUpdate] with Zoom and Tilt level added with the given position.

    fun buildCameraUpdate(latLng: LatLng): CameraUpdate {
        val cameraPosition = CameraPosition.Builder()
        return CameraUpdateFactory.newCameraPosition(cameraPosition)

     * @param position where to draw the []
     * @return the [MarkerOptions] with given properties added to it.

    fun getDriverMarkerOptions(position: LatLng): MarkerOptions {
        val options = getMarkerOptions(R.drawable.car_icon, position)
        return options

    private fun getMarkerOptions(resource: Int, position: LatLng): MarkerOptions {
        return MarkerOptions()


class MarkerAnimationHelper {

    fun animateMarkerToGB(marker: Marker, finalPosition: LatLng, latLngInterpolator: LatLngInterpolator) {
        val startPosition = marker.position
        val handler = Handler()
        val start = SystemClock.uptimeMillis()
        val interpolator = AccelerateDecelerateInterpolator()
        val durationInMs = 2000f : Runnable {
            var elapsed: Long = 0
            var t: Float = 0.toFloat()
            var v: Float = 0.toFloat()
            override fun run() {
                // Calculate progress using interpolator
                elapsed = SystemClock.uptimeMillis() - start
                t = elapsed / durationInMs
                v = interpolator.getInterpolation(t)
                marker.position = latLngInterpolator.interpolate(v, startPosition, finalPosition)
                // Repeat till progress is complete.
                if (t < 1) {
                    // Post again 16ms later.
                    handler.postDelayed(this, 16)

The above MarkerAnimationHelper class animate the driver car Marker from the previous location to user new Location.


interface LatLngInterpolator {

    fun interpolate(fraction: Float, a: LatLng, b: LatLng): LatLng

    class Spherical : LatLngInterpolator {

        override fun interpolate(fraction: Float, a: LatLng, b: LatLng): LatLng {
            val fromLat = toRadians(a.latitude)
            val fromLng = toRadians(a.longitude)
            val toLat = toRadians(b.latitude)
            val toLng = toRadians(b.longitude)
            val cosFromLat = cos(fromLat)
            val cosToLat = cos(toLat)

            // Computes Spherical interpolation coefficients.
            val angle = computeAngleBetween(fromLat, fromLng, toLat, toLng)
            val sinAngle = sin(angle)
            if (sinAngle < 1E-6) {
                return a
            val temp1 = sin((1 - fraction) * angle) / sinAngle
            val temp2 = sin(fraction * angle) / sinAngle

            // Converts from polar to vector and interpolate.
            val x = temp1 * cosFromLat * cos(fromLng) + temp2 * cosToLat * cos(toLng)
            val y = temp1 * cosFromLat * sin(fromLng) + temp2 * cosToLat * sin(toLng)
            val z = temp1 * sin(fromLat) + temp2 * sin(toLat)

            // Converts interpolated vector back to polar.
            val lat = atan2(z, sqrt(x * x + y * y))
            val lng = atan2(y, x)
            return LatLng(toDegrees(lat), toDegrees(lng))

        private fun computeAngleBetween(fromLat: Double, fromLng: Double, toLat: Double, toLng: Double): Double {
            val dLat = fromLat - toLat
            val dLng = fromLng - toLng
            return 2 * asin(sqrt(pow(sin(dLat / 2), 2.0) + cos(fromLat) * cos(toLat) * pow(sin(dLng / 2), 2.0)))

The LatLngInterpolator interface helps to animate the driver car Marker.


class FirebaseHelper constructor(driverId: String) {

    companion object {
        private const val ONLINE_DRIVERS = "online_drivers"

    private val onlineDriverDatabaseReference: DatabaseReference = FirebaseDatabase

    init {

    fun updateDriver(driver: Driver) {
        Log.e("Driver Info", " Updated")

    fun deleteDriver() {

Before going to start to explain FirebaseHelper, I want to show you Firebase Real-time Database Structure.

Firebase Realtime Database

The following explains about FirebaseHelper class.

  1. onlineDriverDatabaseReference: When creating a DatabaseReference, we’re adding two children one for online drivers node and another one for the Driver itself. We need to inform firebase real-time database in which node to update the Diver that’s why I’m setting driverId as a top node and below the is the complete Driver object. The driverId must be unique because it differs in whole online drivers node which specific driver node to update.
  2. updateDriver: Update the Driver new Location to firebase real-time database.
  3. deleteDriver: Removes the driver node from firebase real-time database.


data class Driver(val lat: Double, val lng: Double, val driverId: String = "0000")

You can change driverId with the user primary key any anything which is unique.

Alright, guys, this was all from DriverApp. I’m going to end this article here if you want to see the Passenger app see the next article. If you’ve any queries regarding this post please do comment below.

Download Complete Code

Thank you for being here and keep reading…

Next Part


I’m a mobile product devsigner (i.e. I consider myself as both a developer and a designer) and user experience/interface engineer. I’m an expert on the Android platform and have been recognized as it by the community.


  1. hello, in firebase the database is called online_drivers and 0000 is a table and driverId and lat and the log are attributes.

    my doubt the data that shows the attributes is generated by the app and the user enters that data.

    also to generate the passenger app does not show the car icon.

  2. Hello, I have the same problem that CHITGOKS. Everything seems to work fine, but the Firebase database does not update when I set the “offline” switch of the driver app to “online”. The database just contains:

    appname: null + x

    Also, the passenger app does not recognize the position of the driver (and does not detect that the driver is connected).

    Any help will be appreciated!

    Thank you VERY much Ahsen!

  3. I could run the two applications without problems, but passenger app does not show its location and the device is activated.
    Please help

    • Hey Felipe,
      Please check out that the application has the location permission in settings->apps->Application->Permissions

  4. hi. your app works ok except for one thing. it does not send data to firebase realtime db

    or it just doesnt get saved. its weird because it says

    appname: null + x

    no record from the driver object. any idea?

  5. hello I get this error when I compile the application

    Manifest merger failed : Attribute [email protected] value=( from [] AndroidManifest.xml:22:18-91
    is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(
    Suggestion: add ‘tools:replace=”android:appComponentFactory”‘ to element at AndroidManifest.xml:17:5-46:19 to override.

    • Hey Felipe,
      Please add the following line inside the file.
      # Automatically convert third-party libraries to use AndroidX

  6. Hey Ahsen,
    could you please help me in calculating the complexity of “Main Activity”?
    Thank you.

  7. Hanzala Sohrab Reply

    Everything works fine now, except that the “Total Online Drivers” always remains 0. Do I make any database structure on Firebase. Thank you.

    • No, you don’t need to make any change inside database structure. Just run the driver application on different mobile and turn on the online switch at the top of the driver application. If it still shows you, 0 drivers, then share the code with me on my email. I’ll look into it. You can get the email “About Us” page.

  8. Hanzala Sohrab Reply

    When I enable Google Maps API and then install the app, the app crashes every time.
    When Google Maps API was diabled, it worked but did not show any map.

  9. Hanzala Sohrab Reply

    implementation ‘’
    While adding my app in Firebase, I am told to include the above in app level build.gradle and then I get that error.

  10. Hanzala Sohrab Reply

    After doing everything, I get the following error

    Manifest merger failed : uses-sdk:minSdkVersion 15 cannot be smaller than version 16 declared in library [] /root/.gradle/caches/transforms-2/files-2.1/aab530e535121f58664daf9c9555ce38/AndroidManifest.xml as the library might be using APIs not available in 15
    Suggestion: use a compatible library with a minSdk of at most 15,
    or increase this project’s minSdk version to at least 16,
    or use tools:overrideLibrary=”” to force usage (may lead to runtime failures)

    I increased the project’s minSdk version to 16, but that didn’t work.

  11. hi, ahsen saeed i already success build the apk for java versions but the apps crash after install and i can’t open it.
    can u help me?


  12. Mr Ahsen please help im stuck.
    shows error upon sync
    “File google-services.json is missing. The Google Services Plugin cannot function without it.
    Searched Location: “

    • Ahsen Saeed Reply

      Hey AGA,
      You need to first create a firebase project, then download the google-services.json file in app directory of your Android Project.

    • Ahsen Saeed Reply

      Hey Zareen,
      You don’t need to create firebase database structure. It’ll automatically created for you when you upload your driver data on firebase. You just need to create your java or kotlin class model then simply upload it to firebase.

  13. Anshu Verma Reply

    In Java version of the app, how can I add a user’s name in place of “0000”(driver id) if I am getting the user name from an eddittext .

    • Ahsen Saeed Reply

      Hey Anshu Verma,
      Simple rename the driverId field to name field and pass the edittext name in the constructor and finally upload the object to firebase database.

  14. How could I set the Google user as the Driver ID if I login via Google Sign-In?

    • Ahsen Saeed Reply

      Just pass the driverId in the Driver constructor when creating a driver object.

  15. Anshu Verma Reply

    There is a problem in both Java and kotlin version of the app, whenever I switch on location switch it sends location data to the database perfectly but when I tap the back button and reopen the app the switch is turned off, but the location data is still present in the database and now that data can’t be removed from the database until you uninstall the app from your device. Please tell me how can I fix this.

    • Ahsen Saeed Reply

      Hey Anshu,
      This happens becuase there’s only one Activity in our application. When you press the back button the application quits. So, we need to prevent our application to close down on back button. The following shows how to prevent the application closing.

      public void onBackPressed() {


      public boolean onKeyDown(int keyCode, KeyEvent event) {
      switch (keyCode) {
      case KeyEvent.KEYCODE_BACK:
      return true;
      return false;

      Now try to close down the application with swipe gesture you’ll see the driver info from firebase will remove.

    • Ahsen Saeed Reply

      I’m so sorry for late reply. Yes, I will upload code at the of Sunday night on GitHub.

      • Mr.Ahsen Saeed, I am not able to find the java version code, could you please give me the link, thank you.

      • Mr. Ahsen Saeed, I am not able to find java version, could you please give me the link, thank you.

          • vickey rajak Reply

            Mr. Ahsen Saeed, I got the driver app from the GitHub but I am not able to find java version of Passenger app, could you please give me the link, Thank you.

Write A Comment