How To Make an Android Run Tracking App

Learn how to make an Android run tracking app to show a user’s position on a map, along with their path. By Roberto Orgiu.

5 (1) · 1 Review

Download materials
Save for later
Share

In this tutorial, you’ll learn how to make an Android run tracking app that shows the user’s position on a map, together with their path. This approach is practical when you’re trying to build a tracker for sport sessions, like running or biking. Using the Google Maps API, you can create a tracker with this ability.

Oh, wait… that’s what you’re going to do now!

Along the way, you’ll learn how to:

  • Use the Google Maps API inside your Android project.
  • Get the user’s last recorded position.
  • Get updates on the user’s movements.
  • Recognize how the user is moving.
  • Plot the user’s path on the map.

Before you start, check out this article on how to use maps on Android, or this article about Activity Recognition. It’s always nice to have some prior knowledge of the topic!

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Once you load the project into your IDE, head to the Google Cloud Console and log in with your Google account. On the Credentials page, select a project or create a new one.

Beware, it could take a few minutes!

Select the dropdown at the top for the projects, then create or a select a project.

At the top of the page, click Create Credentials and select API Key. Make sure to copy the API Key because you’ll need it soon. If you get lost in the process, check the official documentation here.

Also, make sure to enable the Maps SDK for Android under the API Library.

Now, back in your IDE, select the two google_maps_api files.

Using the project view, the files are visible in the values folder of res.

The first is for the debug variant and other is for the release. Then paste your API Key where you see this code:

<resources>
  <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string>
</resources>

You’re ready to build your project for the first time! Click the run button and you’ll see this UI:

android run tracking app

There are three data fields at the top of the screen: average pace, elapsed time and distance the user moved in the session. These values change when the user starts to move.

Below is your map. If you don’t see it right away, make sure you correctly pasted your API Key in the previous step and enabled the Maps SDK for Android.

Last but not least, there’s the start and stop button that you’ll use to begin and end tracking the user.

Now, you’re ready to rock!

Location, Location, Location

Before you perform any query related to locations, you need to get your dependencies right.

In the app folder, open build.gradle for the app module. Then paste these lines inside the dependencies lambda:

implementation 'com.google.android.gms:play-services-maps:17.0.1'
implementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'com.google.maps.android:android-maps-utils:2.2.3'
implementation 'com.google.maps.android:maps-utils-ktx:3.2.0'

Now, click Sync project and you’re ready to rock!

You need to get the user’s location. This step is composed of two actions that display the user’s position on a map.

First, you’ll get the user’s coordinates, and then you’ll build the logic to ask for the location permission. Once the user grants this permission, you’ll display their position on the map.

Getting the User’s Location

In the main package, create a new class called LocationProvider and paste this code inside its file:

class LocationProvider(private val activity: AppCompatActivity) {

  //1
  private val client 
    by lazy { LocationServices.getFusedLocationProviderClient(activity) }

  //2
  private val locations = mutableListOf<LatLng>()

  //3
  val liveLocation = MutableLiveData<LatLng>()

  //4
  fun getUserLocation() {
    client.lastLocation.addOnSuccessListener { location ->
      val latLng = LatLng(location.latitude, location.longitude)
      locations.add(latLng)
      liveLocation.value = latLng
    }
  }
}

Here’s a code breakdown:

  1. First, you get the object that you need to get the user’s location. You need the Activity to build it, but since you’ll only invoke it after you ensure your app has the permission, keep it lazy, so that the LocationProvider only creates it when needed.
  2. This line is the list of locations that you’ll need later. At the moment, it only contains the user’s initial position, but that’ll change soon.
  3. Here’s the LiveData that contains the device’s location. You’ll listen to this data in a few minutes!
  4. Finally, here’s your request for the user’s position. You take the client you created lazily and add a listener to it. Whenever it receives a new location, it’ll transform it to a LatLng, which contains the location’s latitude and longitude, add it to the list of the locations and emit it through the LiveData.

Now, you’ll see an IDE warning you that your code isn’t asking for the correct permission. It’s right, for the moment.

So, add this annotation to the top of this class:

@SuppressLint("MissingPermission")

Here, you tell the IDE not to warn you anymore for this class. You’re aware that you don’t have the permission, but you’re about to fix it.

Asking Permission to Get Your User’s Location

Create another class, and call it PermissionManager. Inside it, paste:

class PermissionsManager(
    activity: AppCompatActivity,
    private val locationProvider: LocationProvider) {

  //1 
  private val locationPermissionProvider = activity.registerForActivityResult(
      ActivityResultContracts.RequestPermission()) { granted ->
    if (granted) {
      locationProvider.getUserLocation()
    }
  }

  //2
  fun requestUserLocation() {
    locationPermissionProvider.launch(Manifest.permission.ACCESS_FINE_LOCATION)
  }
}

Here’s a code breakdown:

  1. First, you register a callback on the Activity when the user grants permission. This callback runs and provides a response as soon as the user presses a button in the permission dialog. If the response is positive, you request the device location with the same class you created in the previous step.
  2. Then, you ask for permission. Whenever your app is ready to display the location, you invoke this function, the system will ask for permission and, as soon as the user taps anything, your callback runs.

With that in place, it’s time to test the app!

Testing for the First Time

Now, head back to MapsActivity.kt, and add these lines at the top. Don’t worry about making them too fancy. You’ll remove them soon since you only need them to test the code you just wrote:

private val locationProvider = LocationProvider(this)
private val permissionManager = PermissionsManager(this, locationProvider)

Now, scroll down to the onMapReady method, delete all of its content and replace with:

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

  //1
  locationProvider.liveLocation.observe(this) { latLng ->
    map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f))
  }
    
  //2
  permissionManager.requestUserLocation()

  map.uiSettings.isZoomControlsEnabled = true
}

Here’s a code breakdown:

  1. First, you observe the LiveData you created. Once the LocationProvider gets a new location, it emits it, and you move the camera accordingly.
  2. Next, you use the PermissionManager to ask for the needed permission.

When the map is ready, you get the user’s location. Build and run the app. You’ll see your position appear on your device!

android run tracking app with location

If you’re on an emulator, move your location around in the options or even import a GPX path file to see the app update the location automatically!

android run tracking app emulator

Now that you have the user’s location take it a step further by showing their movement.