Google Maps Integration
A pair of latitude/longitude numbers on a Trail is useful data, but it means nothing
visually to a user until it's actually drawn on a map. This lesson adds the Google Maps SDK to
TrailLog and puts the last two lessons' work — a trail's saved location, and a stream of GPS points
gathered while tracking a hike — onto an actual map.
Adding the SDK and an API Key
The Maps SDK is added as a Gradle dependency, same as any other library:
dependencies {
implementation("com.google.android.gms:play-services-maps:19.0.0")
}
Beyond the dependency, Google Maps also requires an API key, obtained from the
Google Cloud Console by enabling the "Maps SDK for Android" for a project and generating a
credential restricted to your app. That key goes into AndroidManifest.xml, inside the
<application> tag:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY_HERE" />
In a real project this key should be kept out of source control (loaded from a local, untracked properties file at build time) rather than committed directly into the manifest — the placeholder above is only to show where the key ultimately needs to end up.
A Map in the Trail Detail Screen
TrailLog's trail-detail screen — the same one from
Activities & Intents that shows a trail's notes and
photo — gets a SupportMapFragment added to its layout to show where the hike took
place:
<fragment
android:id="@+id/trailMapFragment"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="220dp" />
Getting a GoogleMap Object Asynchronously
The map fragment doesn't hand you a usable GoogleMap object right away — the map's
tiles and underlying SDK need a moment to initialize first. That's why it's fetched through a
callback, getMapAsync { }, rather than returned synchronously:
val mapFragment = supportFragmentManager
.findFragmentById(R.id.trailMapFragment) as SupportMapFragment
mapFragment.getMapAsync { googleMap ->
// googleMap is now ready to use — add markers, move the camera, etc.
showTrailOnMap(googleMap, trail)
}
Adding a Marker for a Trail's Location
Once googleMap is available, dropping a pin at the trail's saved coordinates is a
single call:
private fun showTrailOnMap(googleMap: GoogleMap, trail: Trail) {
val trailLatLng = LatLng(trail.latitude, trail.longitude)
googleMap.addMarker(
MarkerOptions()
.position(trailLatLng)
.title(trail.name)
)
}
Drawing a Polyline for the Full Hike Route
A single marker only shows where a trail ended up being logged. The genuinely satisfying
TrailLog feature is drawing the entire route — every GPS point collected by the continuous location
tracking from Fused Location Provider — connected into one
continuous line with Polyline:
private fun drawHikeRoute(googleMap: GoogleMap, routePoints: List<LatLng>) {
googleMap.addPolyline(
PolylineOptions()
.addAll(routePoints)
.width(8f)
.color(Color.parseColor("#2196F3"))
)
}
routePoints here is simply the list of LatLng values accumulated while
HikeTrackingService was actively collecting location updates — the same data captured
in the previous lesson, now rendered visually as the exact path the hike followed.
Focusing the Camera on a Marker
By default the map opens centered on the globe, not on the trail you actually care about.
animateCamera() smoothly moves the visible viewport to focus on it:
googleMap.animateCamera(
CameraUpdateFactory.newLatLngZoom(trailLatLng, 14f)
)
The second argument is a zoom level — higher numbers zoom in further; 14f is a
reasonable default for viewing a single local trail.
Matching the Map's Style to TrailLog's Theme
By default Google Maps renders with its own standard colors, which can clash with an app's own
theme — particularly the dark mode covered in
Theming & Dark Mode. Google Maps supports custom map
styling through a JSON style document (generated via the Cloud Console's styling tool and loaded
with MapStyleOptions), letting road colors, water, and labels be tuned to match
TrailLog's own palette rather than looking like a visually separate app pasted into a screen.