mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Workout: map improvments
This commit is contained in:
@@ -173,14 +173,14 @@
|
||||
android:name=".activities.DashboardPreferencesActivity"
|
||||
android:label="@string/dashboard_settings"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.maps.MapsTrackActivity"
|
||||
android:label="@string/maps"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.maps.MapsSettingsActivity"
|
||||
android:label="@string/maps_settings"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.maps.MapsTrackActivity"
|
||||
android:label="@string/maps"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.SleepAsAndroidPreferencesActivity"
|
||||
android:label="@string/sleepasandroid_settings"
|
||||
|
||||
+78
-78
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020-2024 José Rebelo, Petr Vaněk
|
||||
/* Copyright (C) 2020-2024 José Rebelo, Petr Vaněk, a0z
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.mapsforge.core.graphics.Paint;
|
||||
import org.mapsforge.core.graphics.Style;
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||
import org.mapsforge.map.android.view.MapView;
|
||||
@@ -41,11 +41,9 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.maps.MapsTrackActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
|
||||
@@ -57,26 +55,31 @@ import nodomain.freeyourgadget.gadgetbridge.util.gpx.GpxParseException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.gpx.GpxParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.maps.MapsManager;
|
||||
|
||||
import static android.graphics.Bitmap.createBitmap;
|
||||
|
||||
|
||||
public class ActivitySummariesGpsFragment extends AbstractGBFragment {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummariesGpsFragment.class);
|
||||
private final int CANVAS_SIZE = 360;
|
||||
|
||||
private MapView mapView;
|
||||
private TextView gpsWarning;
|
||||
private File inputFile;
|
||||
private long downTime;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_gps, container, false);
|
||||
gpsWarning = rootView.findViewById(R.id.gpsWarning);
|
||||
mapView = rootView.findViewById(R.id.activitygpsview);
|
||||
//mapView.getMapScaleBar().setVisible(true);
|
||||
mapView.setBuiltInZoomControls(false);
|
||||
|
||||
MapsManager mapsManager = new MapsManager(requireContext());
|
||||
mapsManager.loadMaps(mapView);
|
||||
|
||||
if (mapsManager.isMapLoaded()) {
|
||||
gpsWarning.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (inputFile != null) {
|
||||
processInBackgroundThread();
|
||||
}
|
||||
@@ -92,47 +95,51 @@ public class ActivitySummariesGpsFragment extends AbstractGBFragment {
|
||||
|
||||
private void processInBackgroundThread() {
|
||||
new Thread(() -> {
|
||||
final ArrayList<GPSCoordinate> points = new ArrayList<>();
|
||||
if (inputFile.getName().endsWith(".gpx")) {
|
||||
try (FileInputStream inputStream = new FileInputStream(inputFile)) {
|
||||
final GpxParser gpxParser = new GpxParser(inputStream);
|
||||
points.addAll(gpxParser.getGpxFile().getPoints());
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to open {}", inputFile, e);
|
||||
return;
|
||||
} catch (final GpxParseException e) {
|
||||
LOG.error("Failed to parse gpx file", e);
|
||||
return;
|
||||
}
|
||||
} else if (inputFile.getName().endsWith(".fit")) {
|
||||
try {
|
||||
FitFile fitFile = FitFile.parseIncoming(inputFile);
|
||||
for (final RecordData record : fitFile.getRecords()) {
|
||||
if (record instanceof FitRecord) {
|
||||
final ActivityPoint activityPoint = ((FitRecord) record).toActivityPoint();
|
||||
if (activityPoint.getLocation() != null) {
|
||||
points.add(activityPoint.getLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to open {}", inputFile, e);
|
||||
return;
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to parse fit file", e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Unknown file type {}", inputFile.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
final List<GPSCoordinate> points = getActivityPoints(inputFile)
|
||||
.stream()
|
||||
.map(ActivityPoint::getLocation)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (!points.isEmpty()) {
|
||||
drawTrack(mapView, points);
|
||||
drawTrack(mapView, (ArrayList<? extends GPSCoordinate>) points);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static List<ActivityPoint> getActivityPoints(final File trackFile) {
|
||||
final List<ActivityPoint> points = new ArrayList<>();
|
||||
if (trackFile == null) {
|
||||
return points;
|
||||
}
|
||||
if (trackFile.getName().endsWith(".gpx")) {
|
||||
try (FileInputStream inputStream = new FileInputStream(trackFile)) {
|
||||
final GpxParser gpxParser = new GpxParser(inputStream);
|
||||
points.addAll(gpxParser.getGpxFile().getActivityPoints());
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to open {}", trackFile, e);
|
||||
} catch (final GpxParseException e) {
|
||||
LOG.error("Failed to parse gpx file", e);
|
||||
}
|
||||
} else if (trackFile.getName().endsWith(".fit")) {
|
||||
try {
|
||||
FitFile fitFile = FitFile.parseIncoming(trackFile);
|
||||
for (final RecordData record : fitFile.getRecords()) {
|
||||
if (record instanceof FitRecord) {
|
||||
points.add(((FitRecord) record).toActivityPoint());
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to open {}", trackFile, e);
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to parse fit file", e);
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Unknown file type {}", trackFile.getName());
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
private void drawTrack(MapView mapView, final ArrayList<? extends GPSCoordinate> trackPoints) {
|
||||
double maxLat = (Collections.max(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||
double minLat = (Collections.min(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||
@@ -142,44 +149,36 @@ public class ActivitySummariesGpsFragment extends AbstractGBFragment {
|
||||
.map(p -> new LatLong(p.getLatitude(), p.getLongitude()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Paint p = AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
p.setColor(R.color.hrv_status_char_line_color);
|
||||
Polyline polyline = new Polyline(p, AndroidGraphicFactory.INSTANCE);
|
||||
polyline.setPoints(latlongs);
|
||||
mapView.addLayer(polyline);
|
||||
Paint paint = AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
paint.setColor(getResources().getColor(R.color.chart_activity_light));
|
||||
paint.setStrokeWidth(8);
|
||||
paint.setStyle(Style.STROKE);
|
||||
Polyline polyline = new Polyline(paint, AndroidGraphicFactory.INSTANCE);
|
||||
polyline.addPoints(latlongs);
|
||||
|
||||
mapView.getLayerManager().getLayers().add(polyline);
|
||||
mapView.getLayerManager().redrawLayers();
|
||||
|
||||
mapView.setCenter(new LatLong(minLat + (maxLat - minLat) / 2, minLon + (maxLon - minLon) / 2));
|
||||
mapView.setZoomLevel((byte) 12);
|
||||
mapView.setZoomLevel((byte) 13);
|
||||
|
||||
mapView.setOnTouchListener((a, b) -> {
|
||||
final Intent startIntent = new Intent(requireContext(), MapsTrackActivity.class);
|
||||
startIntent.putParcelableArrayListExtra("points", trackPoints);
|
||||
requireContext().startActivity(startIntent);
|
||||
final long DOUBLE_TAP_THRESHOLD = 300;
|
||||
mapView.setOnTouchListener((view, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if ((currentTime - downTime) <= DOUBLE_TAP_THRESHOLD) {
|
||||
Intent intent = new Intent(getContext(), MapsTrackActivity.class);
|
||||
intent.putExtra("file", inputFile);
|
||||
startActivity(intent);
|
||||
}
|
||||
downTime = currentTime;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
mapView.setOnClickListener(v -> {
|
||||
final Intent startIntent = new Intent(requireContext(), MapsTrackActivity.class);
|
||||
startIntent.putParcelableArrayListExtra("points", trackPoints);
|
||||
requireContext().startActivity(startIntent);
|
||||
});
|
||||
}
|
||||
|
||||
private Canvas createCanvas(ImageView imageView) {
|
||||
Bitmap bitmap = createBitmap(CANVAS_SIZE, CANVAS_SIZE, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(GBApplication.getWindowBackgroundColor(requireActivity()));
|
||||
//frame around, but it doesn't look so nice
|
||||
/*
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStrokeWidth(0);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setColor(getResources().getColor(R.color.chart_activity_light));
|
||||
canvas.drawRect(0,0,360,360,paint);
|
||||
*/
|
||||
imageView.setImageBitmap(bitmap);
|
||||
imageView.setScaleY(-1f); //flip the canvas
|
||||
|
||||
return canvas;
|
||||
mapView.invalidate();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -187,4 +186,5 @@ public class ActivitySummariesGpsFragment extends AbstractGBFragment {
|
||||
protected CharSequence getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-6
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2024 José Rebelo
|
||||
/* Copyright (C) 2024 José Rebelo, a0z
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@@ -22,34 +22,54 @@ import android.view.MenuItem;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.mapsforge.core.graphics.Paint;
|
||||
import org.mapsforge.core.graphics.Style;
|
||||
import org.mapsforge.core.model.LatLong;
|
||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||
import org.mapsforge.map.android.view.MapView;
|
||||
import org.mapsforge.map.layer.overlay.Polyline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesGpsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.maps.MapsManager;
|
||||
|
||||
public class MapsTrackActivity extends AbstractGBActivity {
|
||||
private MapView mapView;
|
||||
private File file;
|
||||
public static boolean isActivityOpen = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_maps_track);
|
||||
|
||||
if (isActivityOpen) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
isActivityOpen = true;
|
||||
|
||||
mapView = findViewById(R.id.activitygpsview);
|
||||
MapsManager mapsManager = new MapsManager(this);
|
||||
mapsManager.loadMaps(mapView);
|
||||
|
||||
final ArrayList<? extends GPSCoordinate> trackPoints = getIntent().getParcelableArrayListExtra("points");
|
||||
file = (File) getIntent().getExtras().get("file");
|
||||
final List<GPSCoordinate> trackPoints = ActivitySummariesGpsFragment.getActivityPoints(file)
|
||||
.stream()
|
||||
.map(ActivityPoint::getLocation)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (trackPoints.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
double maxLat = (Collections.max(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||
double minLat = (Collections.min(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
|
||||
@@ -59,9 +79,11 @@ public class MapsTrackActivity extends AbstractGBActivity {
|
||||
.map(p -> new LatLong(p.getLatitude(), p.getLongitude()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Paint p = AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
p.setColor(R.color.hrv_status_char_line_color);
|
||||
Polyline polyline = new Polyline(p, AndroidGraphicFactory.INSTANCE);
|
||||
Paint paint = AndroidGraphicFactory.INSTANCE.createPaint();
|
||||
paint.setColor(getResources().getColor(R.color.chart_activity_light));
|
||||
paint.setStrokeWidth(8);
|
||||
paint.setStyle(Style.STROKE);
|
||||
Polyline polyline = new Polyline(paint, AndroidGraphicFactory.INSTANCE);
|
||||
polyline.setPoints(latlongs);
|
||||
mapView.addLayer(polyline);
|
||||
|
||||
@@ -89,4 +111,13 @@ public class MapsTrackActivity extends AbstractGBActivity {
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
synchronized (MapsTrackActivity.class) {
|
||||
isActivityOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ package nodomain.freeyourgadget.gadgetbridge.util.maps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.mapsforge.core.graphics.Canvas;
|
||||
import org.mapsforge.core.graphics.Color;
|
||||
import org.mapsforge.core.model.BoundingBox;
|
||||
import org.mapsforge.core.model.Point;
|
||||
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
|
||||
import org.mapsforge.map.android.util.AndroidUtil;
|
||||
import org.mapsforge.map.android.view.MapView;
|
||||
@@ -34,11 +37,8 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
|
||||
public final class MapsManager {
|
||||
@@ -48,6 +48,8 @@ public final class MapsManager {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private boolean isMapLoaded = false;
|
||||
|
||||
public MapsManager(final Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
@@ -88,6 +90,7 @@ public final class MapsManager {
|
||||
assert inputStream != null;
|
||||
MapFile mapFile = new MapFile(inputStream, 0, null);
|
||||
multiMapDataStore.addMapDataStore(mapFile, true, true);
|
||||
isMapLoaded = true;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -97,13 +100,22 @@ public final class MapsManager {
|
||||
mapView.getModel().displayModel.getTileSize(), 1f,
|
||||
mapView.getModel().frameBufferModel.getOverdrawFactor());
|
||||
|
||||
TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, multiMapDataStore,
|
||||
mapView.getModel().mapViewPosition, AndroidGraphicFactory.INSTANCE);
|
||||
TileRendererLayer tileRendererLayer = new TileRendererLayer(
|
||||
tileCache, multiMapDataStore,
|
||||
mapView.getModel().mapViewPosition, true, false, false, AndroidGraphicFactory.INSTANCE) {
|
||||
@Override
|
||||
public void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) {
|
||||
// Clear the canvas with the desired background color
|
||||
canvas.fillColor(Color.TRANSPARENT);
|
||||
super.draw(boundingBox, zoomLevel, canvas, topLeftPoint);
|
||||
}
|
||||
};
|
||||
tileRendererLayer.setXmlRenderTheme(MapTheme.DEFAULT);
|
||||
|
||||
mapView.getLayerManager().getLayers().add(tileRendererLayer);
|
||||
}
|
||||
|
||||
// TODO on exit mapView.destroyAll();
|
||||
//AndroidGraphicFactory.clearResourceMemoryCache();
|
||||
public boolean isMapLoaded() {
|
||||
return isMapLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.maps.MapsTrackActivity">
|
||||
|
||||
<org.mapsforge.map.android.view.MapView
|
||||
android:id="@+id/activitygpsview"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -17,34 +17,25 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/gps_track"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/gpsWarning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Maps not found" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
android:text="@string/maps_not_found" />
|
||||
<org.mapsforge.map.android.view.MapView
|
||||
android:id="@+id/activitygpsview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" />
|
||||
android:layout_height="300dp"
|
||||
android:orientation="vertical"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -3559,6 +3559,7 @@
|
||||
<string name="pref_garmin_agps_help">The list below contains all URLs requested by the watch for AGPS updates. You can select a file from the phone\'s storage that will be sent to the watch when it requests an update.</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="maps_not_found">Maps not found</string>
|
||||
<string name="toggle_debug_mode">Toggle debug mode</string>
|
||||
<string name="share_debug_info">Share debug info</string>
|
||||
<string name="realtime_settings">Realtime settings</string>
|
||||
|
||||
Reference in New Issue
Block a user