Skip to content

Commit

Permalink
Add playback functionality to GPX layers
Browse files Browse the repository at this point in the history
Fixes #1217
  • Loading branch information
simonpoole committed Mar 6, 2022
1 parent 2cf77a4 commit 43fadd0
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 13 deletions.
6 changes: 2 additions & 4 deletions src/main/java/de/blau/android/Map.java
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,14 @@ public void setUpLayers(@NonNull Context ctx) {
case GPX:
if (contentId != null) {
layer = new de.blau.android.layer.gpx.MapOverlay(this, contentId);
final String recordingId = ctx.getString(R.string.layer_gpx_recording);
if (contentId.equals(recordingId)) {
if (ctx.getString(R.string.layer_gpx_recording).equals(contentId)) {
if (getTracker() != null) {
((de.blau.android.layer.gpx.MapOverlay) layer).setTrack(getTracker().getTrack());
((de.blau.android.layer.gpx.MapOverlay) layer).setName(contentId);
} else {
// we don't want to display the recording layer if the service isn't running
// for consistency reasons this implies that we need to delete the layer
db.deleteLayer(LayerType.GPX, recordingId);
db.deleteLayer(LayerType.GPX, contentId);
continue;
}
} else if (!((de.blau.android.layer.gpx.MapOverlay) layer).fromFile(ctx, Uri.parse(contentId), true, null)) {
Expand All @@ -280,7 +279,6 @@ public void setUpLayers(@NonNull Context ctx) {
case GEOJSON:
if (contentId != null) {
layer = new de.blau.android.layer.geojson.MapOverlay(this);

try {
if (!((de.blau.android.layer.geojson.MapOverlay) layer).loadGeoJsonFile(ctx, Uri.parse(contentId), true)) {
// other error, has already been toasted
Expand Down
33 changes: 32 additions & 1 deletion src/main/java/de/blau/android/dialogs/Layers.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

Expand All @@ -15,6 +16,8 @@
import android.content.DialogInterface;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
Expand Down Expand Up @@ -91,6 +94,7 @@
import de.blau.android.resources.TileLayerSource.Category;
import de.blau.android.resources.TileLayerSource.TileType;
import de.blau.android.resources.WmsEndpointDatabaseView;
import de.blau.android.services.TrackerService;
import de.blau.android.util.Density;
import de.blau.android.util.ExecutorTask;
import de.blau.android.util.FileUtil;
Expand Down Expand Up @@ -935,10 +939,37 @@ public boolean save(Uri fileUri) {
});
return true;
});

if (activity instanceof Main) {
item.setEnabled(((Main) activity).isStoragePermissionGranted());
}
if (!activity.getString(R.string.layer_gpx_recording).equals(layer.getContentId())) {
item = menu.add(R.string.layer_start_playback);
item.setOnMenuItemClickListener(unused -> {
if (layer != null) {
((de.blau.android.layer.gpx.MapOverlay) layer).startPlayback();
}
return true;
});
item.setEnabled(!((de.blau.android.layer.gpx.MapOverlay) layer).isPlaying());

item = menu.add(R.string.layer_pause_playback);
item.setOnMenuItemClickListener(unused -> {
if (layer != null) {
((de.blau.android.layer.gpx.MapOverlay) layer).pausePlayback();
}
return true;
});
item.setEnabled(((de.blau.android.layer.gpx.MapOverlay) layer).isPlaying());

item = menu.add(R.string.layer_stop_playback);
item.setOnMenuItemClickListener(unused -> {
if (layer != null) {
((de.blau.android.layer.gpx.MapOverlay) layer).stopPlayback();
}
return true;
});
item.setEnabled(!((de.blau.android.layer.gpx.MapOverlay) layer).isStopped());
}
}
MenuItem item = menu.add(R.string.move_up);
item.setOnMenuItemClickListener(unused -> {
Expand Down
25 changes: 19 additions & 6 deletions src/main/java/de/blau/android/gpx/TrackPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,32 @@ private byte encodeFlags(boolean isNewSegment) {
}

/**
* Check if this ibject marks the beginning of a new segment
* Check if this object marks the beginning of a new segment
*
* @return true if a new segment starts here
*/
public boolean isNewSegment() {
return (flags & FLAG_NEWSEGMENT) > 0;
}

@Override
public boolean isInterrupted() {
return isNewSegment();
}

/**
* Fill a Location object with our values
*
* @param loc the Location we want to set the vaules on
*/
public void toLocation(@NonNull Location loc) {
loc.reset();
loc.setLongitude(longitude);
loc.setLatitude(latitude);
loc.setAltitude(altitude);
loc.setTime(time);
}

/**
* Adds a GPX trkpt (track point) tag to the given serializer (synchronized due to use of calendarInstance)
*
Expand All @@ -239,9 +257,4 @@ public synchronized void toXml(@NonNull XmlSerializer serializer, @NonNull GpxTi
public String toString() {
return String.format(Locale.US, "%f, %f", latitude, longitude);
}

@Override
public boolean isInterrupted() {
return isNewSegment();
}
}
127 changes: 126 additions & 1 deletion src/main/java/de/blau/android/layer/gpx/MapOverlay.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import de.blau.android.App;
import de.blau.android.Logic;
import de.blau.android.Main;
import de.blau.android.Map;
import de.blau.android.PostAsyncActionHandler;
import de.blau.android.R;
Expand All @@ -42,8 +45,10 @@
import de.blau.android.resources.DataStyle;
import de.blau.android.resources.DataStyle.FeatureStyle;
import de.blau.android.resources.symbols.TriangleDown;
import de.blau.android.services.TrackerService;
import de.blau.android.util.ExecutorTask;
import de.blau.android.util.GeoMath;
import de.blau.android.util.PlaybackTask;
import de.blau.android.util.SavingHelper;
import de.blau.android.util.SelectFile;
import de.blau.android.util.SerializablePaint;
Expand Down Expand Up @@ -72,6 +77,8 @@ public class MapOverlay extends StyleableLayer implements Serializable, ExtentIn

private transient Track track;

private transient PlaybackTask<Void, Void, Void> playbackTask = null;

private SerializablePaint wayPointPaint;
private String labelKey;
private String contentId; // could potentially be transient
Expand Down Expand Up @@ -194,7 +201,7 @@ private void drawTrackPoints(@NonNull Canvas canvas) {
*/
private void drawWayPoints(@NonNull Canvas canvas) {
WayPoint[] wayPoints = track.getWayPoints();
if (wayPoints.length != 0 && symbolPath != null) {
if (symbolPath != null) {
ViewBox viewBox = map.getViewBox();
int width = map.getWidth();
int height = map.getHeight();
Expand Down Expand Up @@ -479,4 +486,122 @@ protected void discardLayer(Context context) {
}
map.invalidate();
}

/**
* Start/resume playback of this track
*/
public void startPlayback() {
if (playbackTask != null) {
playbackTask.resume();
return;
}
playbackTask = new GpxPlayback();
playbackTask.execute();
}

private class GpxPlayback extends PlaybackTask<Void, Void, Void> {

/**
* Create a new instance
*/
public GpxPlayback() {
super(App.getLogic().getExecutorService(), App.getLogic().getHandler());
}

boolean paused = false;

@Override
protected Void doInBackground(Void input) throws Exception {
Context context = MapOverlay.this.map.getContext();
if (context instanceof Main) {
TrackerService tracker = ((Main) context).getTracker();
final Track t = getTrack();
if (t != null) {
Location loc = new Location(LocationManager.GPS_PROVIDER);
for (TrackPoint tp : t.getTrackPoints()) {
while (paused && !isCancelled()) {
sleep();
}

if (isCancelled()) {
break;
}

tp.toLocation(loc);
tracker.gpsListener.onLocationChanged(loc);
sleep();
}
}
}
return null;
}

/**
*
*/
public void sleep() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { // NOSONAR
//
}
}

@Override
protected void onPostExecute(Void output) {
playbackTask = null;
}

@Override
public void pause() {
paused = true;
}

@Override
public void resume() {
paused = false;
}

@Override
public boolean isPaused() {
return paused;
}

}

/**
* Pause playback
*/
public void pausePlayback() {
if (playbackTask != null) {
playbackTask.pause();
}
}

/**
* Check if we are playing the track
*
* @return true if the track is being played
*/
public boolean isPlaying() {
return playbackTask != null && !playbackTask.isPaused();
}

/**
* Check if we are not playing the track
*
* @return true if we are not playing the track
*/
public boolean isStopped() {
return playbackTask == null;
}

/**
* Stop playing the track
*/
public void stopPlayback() {
if (playbackTask != null) {
playbackTask.cancel();
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/de/blau/android/services/TrackerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ public TrackerService getService() {
}
}

LocationListener gpsListener = new LocationListener() {
public LocationListener gpsListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (source == GpsSource.INTERNAL) {
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/de/blau/android/util/PlaybackTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.blau.android.util;

import java.util.concurrent.ExecutorService;

import android.os.Handler;
import androidx.annotation.NonNull;

public abstract class PlaybackTask<I, P, O> extends ExecutorTask<I, P, O> {
/**
* Create a new instance
*
* @param executorService the ExecutorService to use
* @param handler the Handler to use
*/
protected PlaybackTask(@NonNull ExecutorService executorService, @NonNull Handler handler) {
super(executorService, handler);
}

/**
* Pause playback
*/
public abstract void pause();

/**
* Resume playback
*/
public abstract void resume();

/**
* Check if playback is paused
*
* @return true if playback is paused
*/
public abstract boolean isPaused();
}
3 changes: 3 additions & 0 deletions src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,9 @@
<string name="layer_add_gpx">Add layer from GPX file</string>
<string name="layer_download_track">Download GPX track</string>
<string name="layer_available_tracks">Available GPX tracks</string>
<string name="layer_start_playback">Start playback</string>
<string name="layer_pause_playback">Pause playback</string>
<string name="layer_stop_playback">Stop playback</string>
<!-- Layer info dialog -->
<string name="layer_info_min_zoom">Min zoom</string>
<string name="layer_info_max_zoom">Max zoom</string>
Expand Down

0 comments on commit 43fadd0

Please sign in to comment.