diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 5f350cb5..e5726f2d 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -28,7 +28,19 @@ jobs:
         run: chmod +x gradlew
 
       - name: Build with Gradle
-        run: ./gradlew build --stacktrace
+        run: ./gradlew assembleDebug --stacktrace
+
+      - name: Rename output APK
+        run: |
+          DATE=$(date +'%Y%m%d-%H%M')
+          mv app/build/outputs/apk/debug/app-debug.apk app/build/outputs/apk/debug/OSMTracker-debug-$DATE.apk
+          echo "ARTIFACT_DATE=$DATE" >> $GITHUB_ENV
+
+      - name: Upload a Build Artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: debug-${{ env.ARTIFACT_DATE }}
+          path: app/build/outputs/apk/debug/OSMTracker-debug-*.apk
 
       - name: Run unit tests and jacoco coverage
         run: ./gradlew testDebugUnitTest jacocoTestReport --stacktrace
diff --git a/app/build.gradle b/app/build.gradle
index ae56b488..c66d293b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -91,7 +91,7 @@ dependencies {
 
     //implementation 'org.apache.httpcomponents:httpmime:4.5.6'
 
-    implementation 'org.osmdroid:osmdroid-android:6.1.5'
+    implementation 'org.osmdroid:osmdroid-android:6.1.20'
     //implementation 'org.apache.httpcomponents:httpcore:4.4.13'
 
     //implementation 'oauth.signpost:signpost-commonshttp4:1.2.1.2'
diff --git a/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java b/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
index 4ee496f6..678db42a 100644
--- a/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
+++ b/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
@@ -1,24 +1,5 @@
 package net.osmtracker.activity;
 
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.overlay.WayPointsOverlay;
-
-import org.osmdroid.api.IMapController;
-import org.osmdroid.config.Configuration;
-import org.osmdroid.tileprovider.tilesource.ITileSource;
-import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
-import org.osmdroid.util.GeoPoint;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.PathOverlay;
-import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
-import org.osmdroid.views.overlay.ScaleBarOverlay;
-
 import android.app.Activity;
 import android.content.ContentUris;
 import android.content.Intent;
@@ -26,6 +7,7 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.graphics.Color;
+import android.graphics.Paint;
 import android.os.Bundle;
 import android.os.Handler;
 import android.preference.PreferenceManager;
@@ -35,8 +17,23 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.overlay.WayPointsOverlay;
+
+import org.osmdroid.api.IMapController;
+import org.osmdroid.config.Configuration;
+import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
+import org.osmdroid.util.GeoPoint;
+import org.osmdroid.views.MapView;
+import org.osmdroid.views.overlay.Polyline;
+import org.osmdroid.views.overlay.ScaleBarOverlay;
+import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Display current track over an OSM map.
@@ -51,33 +48,30 @@
 public class DisplayTrackMap extends Activity {
 
 	private static final String TAG = DisplayTrackMap.class.getSimpleName();
-	
+
 	/**
 	 * Key for keeping the zoom level in the saved instance bundle
 	 */
 	private static final String CURRENT_ZOOM = "currentZoom";
 
 	/**
-	 *  Key for keeping scrolled left position of OSM view activity re-creation
-	 * 
+	 * Key for keeping scrolled left position of OSM view activity re-creation
 	 */
 	private static final String CURRENT_SCROLL_X = "currentScrollX";
 
-	/** 
+	/**
 	 * Key for keeping scrolled top position of OSM view across activity re-creation
-	 * 
-	 */ 
+	 */
 	private static final String CURRENT_SCROLL_Y = "currentScrollY";
 
 	/**
-	 *  Key for keeping whether the map display should be centered to the gps location 
-	 * 
+	 * Key for keeping whether the map display should be centered to the gps location
 	 */
 	private static final String CURRENT_CENTER_TO_GPS_POS = "currentCenterToGpsPos";
 
 	/**
-	 *  Key for keeping whether the map display was zoomed and centered
-	 *  on an old track id loaded from the database (boolean {@link #zoomedToTrackAlready})
+	 * Key for keeping whether the map display was zoomed and centered
+	 * on an old track id loaded from the database (boolean {@link #zoomedToTrackAlready})
 	 */
 	private static final String CURRENT_ZOOMED_TO_TRACK = "currentZoomedToTrack";
 
@@ -89,30 +83,30 @@ public class DisplayTrackMap extends Activity {
 	/**
 	 * Default zoom level
 	 */
-	private static final int DEFAULT_ZOOM  = 16;
+	private static final int DEFAULT_ZOOM = 16;
 
 	/**
 	 * Main OSM view
 	 */
 	private MapView osmView;
-	
+
 	/**
 	 * Controller to interact with view
 	 */
 	private IMapController osmViewController;
-	
+
 	/**
 	 * OSM view overlay that displays current location
 	 */
 	private SimpleLocationOverlay myLocationOverlay;
-	
+
 	/**
 	 * OSM view overlay that displays current path
 	 */
-	private PathOverlay pathOverlay;
+	private Polyline polyline;
 
 	/**
-	 * OSM view overlay that displays waypoints 
+	 * OSM view overlay that displays waypoints
 	 */
 	private WayPointsOverlay wayPointsOverlay;
 
@@ -120,23 +114,23 @@ public class DisplayTrackMap extends Activity {
 	 * OSM view overlay for the map scale bar
 	 */
 	private ScaleBarOverlay scaleBarOverlay;
-	
+
 	/**
 	 * Current track id
 	 */
 	private long currentTrackId;
-	
+
 	/**
-	 * whether the map display should be centered to the gps location 
+	 * whether the map display should be centered to the gps location
 	 */
 	private boolean centerToGpsPos = true;
-	
+
 	/**
 	 * whether the map display was already zoomed and centered
 	 * on an old track loaded from the database (should be done only once).
 	 */
 	private boolean zoomedToTrackAlready = false;
-	
+
 	/**
 	 * the last position we know
 	 */
@@ -145,12 +139,12 @@ public class DisplayTrackMap extends Activity {
 	/**
 	 * The row id of the last location read from the database that has been added to the
 	 * list of layout points. Using this we to reduce DB load by only reading new points.
-	 * Initially null, to indicate that no data has yet been read.  
+	 * Initially null, to indicate that no data has yet been read.
 	 */
 	private Integer lastTrackPointIdProcessed = null;
-	
+
 	/**
-	 * Observes changes on trackpoints
+	 * Observes changes on track points
 	 */
 	private ContentObserver trackpointContentObserver;
 
@@ -173,14 +167,16 @@ public void onCreate(Bundle savedInstanceState) {
 
 		// Initialize OSM view
 		Configuration.getInstance().load(this, prefs);
-		osmView = (MapView) findViewById(R.id.displaytrackmap_osmView);
-		osmView.setMultiTouchControls(true);  // pinch to zoom
+
+		osmView = findViewById(R.id.displaytrackmap_osmView);
+		// pinch to zoom
+		osmView.setMultiTouchControls(true);
 		// we'll use osmView to define if the screen is always on or not
 		osmView.setKeepScreenOn(prefs.getBoolean(OSMTracker.Preferences.KEY_UI_DISPLAY_KEEP_ON, OSMTracker.Preferences.VAL_UI_DISPLAY_KEEP_ON));
 		osmViewController = osmView.getController();
 
 		// Check if there is a saved zoom level
-		if(savedInstanceState != null) {
+		if (savedInstanceState != null) {
 			osmViewController.setZoom(savedInstanceState.getInt(CURRENT_ZOOM, DEFAULT_ZOOM));
 			osmView.scrollTo(savedInstanceState.getInt(CURRENT_SCROLL_X, 0),
 					savedInstanceState.getInt(CURRENT_SCROLL_Y, 0));
@@ -194,11 +190,11 @@ public void onCreate(Bundle savedInstanceState) {
 
 		selectTileSource();
 
-        setTileDpiScaling();
+		setTileDpiScaling();
 
 		createOverlays();
 
-		// Create content observer for trackpoints
+		// Create content observer for track points
 		trackpointContentObserver = new ContentObserver(new Handler()) {
 			@Override
 			public void onChange(boolean selfChange) {
@@ -207,18 +203,8 @@ public void onChange(boolean selfChange) {
 		};
 
 		// Register listeners for zoom buttons
-		findViewById(R.id.displaytrackmap_imgZoomIn).setOnClickListener( new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				osmViewController.zoomIn();
-			}
-		});
-		findViewById(R.id.displaytrackmap_imgZoomOut).setOnClickListener( new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				osmViewController.zoomOut();
-			}
-		});
+		findViewById(R.id.displaytrackmap_imgZoomIn).setOnClickListener(v -> osmViewController.zoomIn());
+		findViewById(R.id.displaytrackmap_imgZoomOut).setOnClickListener(v -> osmViewController.zoomOut());
 	}
 
 	/**
@@ -231,14 +217,14 @@ public void selectTileSource() {
 		osmView.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
 	}
 
-    /**
-     * Make text on map better readable on high DPI displays
-     */
-    public void setTileDpiScaling () {
-        osmView.setTilesScaledToDpi(true);
-    }
+	/**
+	 * Make text on map better readable on high DPI displays
+	 */
+	public void setTileDpiScaling() {
+		osmView.setTilesScaledToDpi(true);
+	}
+
 
-	
 //	/**
 //	 * Returns a ITileSource for the map according to the selected mapTile
 //	 * String. The default is mapnik.
@@ -268,20 +254,17 @@ protected void onSaveInstanceState(Bundle outState) {
 		super.onSaveInstanceState(outState);
 	}
 
-
 	@Override
 	protected void onResume() {
-
 		super.onResume();
 		resumeActivity();
-
 	}
 
-	private void resumeActivity(){
+	private void resumeActivity() {
 		// setKeepScreenOn depending on user's preferences
 		osmView.setKeepScreenOn(prefs.getBoolean(OSMTracker.Preferences.KEY_UI_DISPLAY_KEEP_ON, OSMTracker.Preferences.VAL_UI_DISPLAY_KEEP_ON));
 
-		// Register content observer for any trackpoint changes
+		// Register content observer for any track point changes
 		getContentResolver().registerContentObserver(
 				TrackContentProvider.trackPointsUri(currentTrackId),
 				true, trackpointContentObserver);
@@ -297,19 +280,18 @@ private void resumeActivity(){
 		selectTileSource();
 
 		setTileDpiScaling();
-		
+
 		// Refresh way points
 		wayPointsOverlay.refresh();
-
 	}
 
 	@Override
 	protected void onPause() {
 		// Unregister content observer
 		getContentResolver().unregisterContentObserver(trackpointContentObserver);
-		
+
 		// Clear the points list.
-		pathOverlay.clearPath();
+		polyline.setPoints(new ArrayList<>());
 
 		super.onPause();
 	}
@@ -322,46 +304,43 @@ protected void onStop() {
 		SharedPreferences settings = getPreferences(MODE_PRIVATE);
 		SharedPreferences.Editor editor = settings.edit();
 		editor.putInt(LAST_ZOOM, osmView.getZoomLevel());
-		editor.commit();
+		editor.apply();
 	}
 
 	@Override
 	public boolean onCreateOptionsMenu(Menu menu) {
 		MenuInflater inflater = getMenuInflater();
-		 inflater.inflate(R.menu.displaytrackmap_menu, menu);
+		inflater.inflate(R.menu.displaytrackmap_menu, menu);
 		return super.onCreateOptionsMenu(menu);
 	}
 
 
 	@Override
 	public boolean onPrepareOptionsMenu(Menu menu) {
-		menu.findItem(R.id.displaytrackmap_menu_center_to_gps).setEnabled( (!centerToGpsPos && currentPosition != null ) );
+		menu.findItem(R.id.displaytrackmap_menu_center_to_gps).setEnabled((!centerToGpsPos && currentPosition != null));
 		return super.onPrepareOptionsMenu(menu);
 	}
 
-	
-
 	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
-		switch(item.getItemId()){
-		case R.id.displaytrackmap_menu_center_to_gps:
-			centerToGpsPos = true;
-			if(currentPosition != null){
-				osmViewController.animateTo(currentPosition);
-			}
-			break;
-		case R.id.displaytrackmap_menu_settings:
-			// Start settings activity
-			startActivity(new Intent(this, Preferences.class));
-			break;
+		switch (item.getItemId()) {
+			case R.id.displaytrackmap_menu_center_to_gps:
+				centerToGpsPos = true;
+				if (currentPosition != null) {
+					osmViewController.animateTo(currentPosition);
+				}
+				break;
+			case R.id.displaytrackmap_menu_settings:
+				// Start settings activity
+				startActivity(new Intent(this, Preferences.class));
+				break;
 		}
 		return super.onOptionsItemSelected(item);
 	}
 
-
 	@Override
 	public boolean onTouchEvent(MotionEvent event) {
-		switch(event.getAction()){
+		switch (event.getAction()) {
 			case MotionEvent.ACTION_MOVE:
 				if (currentPosition != null)
 					centerToGpsPos = false;
@@ -370,7 +349,6 @@ public boolean onTouchEvent(MotionEvent event) {
 		return super.onTouchEvent(event);
 	}
 
-
 	/**
 	 * Creates overlays over the OSM view
 	 */
@@ -379,20 +357,22 @@ private void createOverlays() {
 		this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
 		// set with to hopefully DPI independent 0.5mm
- 		pathOverlay = new PathOverlay(Color.BLUE, (float)(metrics.densityDpi / 25.4 / 2),this);
+		polyline = new Polyline();
+		Paint paint = polyline.getOutlinePaint();
+		paint.setColor(Color.BLUE);
+		paint.setStrokeWidth((float) (metrics.densityDpi / 25.4 / 2));
+		osmView.getOverlayManager().add(polyline);
 
-		osmView.getOverlays().add(pathOverlay);
-		
 		myLocationOverlay = new SimpleLocationOverlay(this);
 		osmView.getOverlays().add(myLocationOverlay);
-		
+
 		wayPointsOverlay = new WayPointsOverlay(this, currentTrackId);
 		osmView.getOverlays().add(wayPointsOverlay);
 
 		scaleBarOverlay = new ScaleBarOverlay(osmView);
 		osmView.getOverlays().add(scaleBarOverlay);
 	}
-	
+
 	/**
 	 * On track path changed, update the two overlays and repaint view.
 	 * If {@link #lastTrackPointIdProcessed} is null, this is the initial call
@@ -404,7 +384,7 @@ private void pathChanged() {
 		if (isFinishing()) {
 			return;
 		}
-		
+
 		// See if the track is active.
 		// If not, we'll calculate initial track bounds
 		// while retrieving from the database.
@@ -412,18 +392,21 @@ private void pathChanged() {
 		boolean doInitialBoundsCalc = false;
 		double minLat = 91.0, minLon = 181.0;
 		double maxLat = -91.0, maxLon = -181.0;
-		if ((! zoomedToTrackAlready) && (lastTrackPointIdProcessed == null)) {
+		if ((!zoomedToTrackAlready) && (lastTrackPointIdProcessed == null)) {
 			final String[] proj_active = {TrackContentProvider.Schema.COL_ACTIVE};
 			Cursor cursor = getContentResolver().query(
-				ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, currentTrackId),
-				proj_active, null, null, null);
-			if (cursor.moveToFirst()) {
-				doInitialBoundsCalc =
-					(cursor.getInt(cursor.getColumnIndex(TrackContentProvider.Schema.COL_ACTIVE)) == TrackContentProvider.Schema.VAL_TRACK_INACTIVE);
+					ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, currentTrackId),
+					proj_active, null, null, null);
+			if (cursor != null && cursor.moveToFirst()) {
+				int colIndex = cursor.getColumnIndex(TrackContentProvider.Schema.COL_ACTIVE);
+				if (colIndex != -1) {
+					doInitialBoundsCalc =
+							(cursor.getInt(colIndex) == TrackContentProvider.Schema.VAL_TRACK_INACTIVE);
+				}
+				cursor.close();
 			}
-			cursor.close();
 		}
-		
+
 		// Projection: The columns to retrieve. Here, we want the latitude, 
 		// longitude and primary key only
 		String[] projection = {TrackContentProvider.Schema.COL_LATITUDE, TrackContentProvider.Schema.COL_LONGITUDE, TrackContentProvider.Schema.COL_ID};
@@ -431,71 +414,69 @@ private void pathChanged() {
 		String selection = null;
 		// SelectionArgs: The parameter replacements to use for the '?' in the selection		
 		String[] selectionArgs = null;
-		
-		  // Only request the track points that we have not seen yet
+
+		// Only request the track points that we have not seen yet
 		// If we have processed any track points in this session then
 		// lastTrackPointIdProcessed will not be null. We only want 
 		// to see data from rows with a primary key greater than lastTrackPointIdProcessed  
 		if (lastTrackPointIdProcessed != null) {
 			selection = TrackContentProvider.Schema.COL_ID + " > ?";
-			List<String> selectionArgsList  = new ArrayList<String>();
+			List<String> selectionArgsList = new ArrayList<>();
 			selectionArgsList.add(lastTrackPointIdProcessed.toString());
-			
-			selectionArgs = selectionArgsList.toArray(new String[1]); 
+			selectionArgs = selectionArgsList.toArray(new String[1]);
 		}
 
 		// Retrieve any points we have not yet seen
 		Cursor c = getContentResolver().query(
-			TrackContentProvider.trackPointsUri(currentTrackId),
-			projection, selection, selectionArgs, TrackContentProvider.Schema.COL_ID + " asc");
-		
-		int numberOfPointsRetrieved = c.getCount();
-		if (numberOfPointsRetrieved > 0 ) {
-			c.moveToFirst();
-			double lastLat = 0;
-			double lastLon = 0;
-			int primaryKeyColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_ID);
-			int latitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE);
-			int longitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE);
-		
-			// Add each new point to the track
-			while(!c.isAfterLast()) {			
-				lastLat = c.getDouble(latitudeColumnIndex);
-				lastLon = c.getDouble(longitudeColumnIndex);
-				lastTrackPointIdProcessed = c.getInt(primaryKeyColumnIndex);
-				pathOverlay.addPoint((int)(lastLat * 1e6), (int)(lastLon * 1e6));
-				if (doInitialBoundsCalc) {
-					if (lastLat < minLat)  minLat = lastLat;
-					if (lastLon < minLon)  minLon = lastLon;
-					if (lastLat > maxLat)  maxLat = lastLat;
-					if (lastLon > maxLon)  maxLon = lastLon;
+				TrackContentProvider.trackPointsUri(currentTrackId),
+				projection, selection, selectionArgs, TrackContentProvider.Schema.COL_ID + " asc");
+
+		if (c != null) {
+			int numberOfPointsRetrieved = c.getCount();
+			if (numberOfPointsRetrieved > 0) {
+				c.moveToFirst();
+				double lastLat = 0;
+				double lastLon = 0;
+				int primaryKeyColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_ID);
+				int latitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE);
+				int longitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE);
+
+				// Add each new point to the track
+				while (!c.isAfterLast()) {
+					lastLat = c.getDouble(latitudeColumnIndex);
+					lastLon = c.getDouble(longitudeColumnIndex);
+					lastTrackPointIdProcessed = c.getInt(primaryKeyColumnIndex);
+					polyline.addPoint(new GeoPoint(lastLat, lastLon));
+					if (doInitialBoundsCalc) {
+						if (lastLat < minLat) minLat = lastLat;
+						if (lastLon < minLon) minLon = lastLon;
+						if (lastLat > maxLat) maxLat = lastLat;
+						if (lastLon > maxLon) maxLon = lastLon;
+					}
+					c.moveToNext();
 				}
-				c.moveToNext();
-			}		
-		
-			// Last point is current position.
-			currentPosition = new GeoPoint(lastLat, lastLon); 
-			myLocationOverlay.setLocation(currentPosition);		
-			if(centerToGpsPos) {
-				osmViewController.setCenter(currentPosition);
-			}
-		
-			// Repaint
-			osmView.invalidate();
-			if (doInitialBoundsCalc && (numberOfPointsRetrieved > 1)) {
-				// osmdroid-3.0.8 hangs if we directly call zoomToSpan during initial onResume,
-				// so post a Runnable instead for after it's done initializing.
-				final double north = maxLat, east = maxLon, south = minLat, west = minLon;
-				osmView.post(new Runnable() {
-					@Override
-					public void run() {
-						osmViewController.zoomToSpan((int) (north-south), (int) (east-west));
+
+				// Last point is current position.
+				currentPosition = new GeoPoint(lastLat, lastLon);
+				myLocationOverlay.setLocation(currentPosition);
+				if (centerToGpsPos) {
+					osmViewController.setCenter(currentPosition);
+				}
+
+				// Repaint
+				osmView.invalidate();
+				if (doInitialBoundsCalc && (numberOfPointsRetrieved > 1)) {
+					// osmdroid-3.0.8 hangs if we directly call zoomToSpan during initial onResume,
+					// so post a Runnable instead for after it's done initializing.
+					final double north = maxLat, east = maxLon, south = minLat, west = minLon;
+					osmView.post(() -> {
+						osmViewController.zoomToSpan((int) (north - south), (int) (east - west));
 						osmViewController.setCenter(new GeoPoint((north + south) / 2, (east + west) / 2));
 						zoomedToTrackAlready = true;
-					}
-				});
+					});
+				}
 			}
+			c.close();
 		}
-		c.close();
 	}
 }