forked from upstream-mirrors/OpenTracks
* fix #1957 add CreateMarkerActivity to receive external marker
This commit is contained in:
@@ -88,9 +88,9 @@ public class TestDataUtil {
|
|||||||
stats.setTotalDistance(Distance.of(0));
|
stats.setTotalDistance(Distance.of(0));
|
||||||
stats.setTotalTime(Duration.ofMillis(0));
|
stats.setTotalTime(Duration.ofMillis(0));
|
||||||
List<Marker> markers = List.of(
|
List<Marker> markers = List.of(
|
||||||
new Marker("Marker 1", "Marker description 1", "Marker category 3", "", trackId, stats, trackPoints.get(1), null),
|
new Marker("Marker 1", "Marker description 1", "Marker category 3", "", trackId, trackPoints.get(1), null),
|
||||||
new Marker("Marker 2", "Marker description 2", "Marker category 3", "", trackId, stats, trackPoints.get(4), null),
|
new Marker("Marker 2", "Marker description 2", "Marker category 3", "", trackId, trackPoints.get(4), null),
|
||||||
new Marker("Marker 3", "Marker description 3", "Marker category 3", "", trackId, stats, trackPoints.get(5), null)
|
new Marker("Marker 3", "Marker description 3", "Marker category 3", "", trackId, trackPoints.get(5), null)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new TrackData(track, trackPoints, markers);
|
return new TrackData(track, trackPoints, markers);
|
||||||
@@ -152,7 +152,7 @@ public class TestDataUtil {
|
|||||||
stats.setTotalDistance(Distance.of(0));
|
stats.setTotalDistance(Distance.of(0));
|
||||||
stats.setTotalTime(Duration.ofMillis(0));
|
stats.setTotalTime(Duration.ofMillis(0));
|
||||||
|
|
||||||
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, stats, trackPoint, photoUrl);
|
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, trackPoint, photoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||||
|
|||||||
+2
-2
@@ -139,7 +139,7 @@ public class ExportImportTest {
|
|||||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
|
||||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
contentProviderUtils.insertMarker(new Marker("Marker 1", "Marker 1 desc", "Marker 1 category", null, trackId, service.getLastStoredTrackPointWithLocation(), ""));
|
||||||
|
|
||||||
// A sensor-only TrackPoint
|
// A sensor-only TrackPoint
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||||
@@ -154,7 +154,7 @@ public class ExportImportTest {
|
|||||||
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
|
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
|
||||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
contentProviderUtils.insertMarker(new Marker("Marker 2", "Marker 2 desc", "Marker 2 category", null, trackId, service.getLastStoredTrackPointWithLocation(), ""));
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||||
|
|||||||
+1
-1
@@ -166,7 +166,7 @@ public class KMLTrackImporterTest {
|
|||||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||||
|
|
||||||
// 2. markers
|
// 2. markers
|
||||||
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
|
assertEquals(1, contentProviderUtils.getMarkers(importTrackId).size());
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
|
|||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package de.dennisguse.opentracks.services;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import androidx.test.filters.MediumTest;
|
|
||||||
import androidx.test.rule.ServiceTestRule;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class TrackRecordingServiceMarkerTest {
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
|
||||||
|
|
||||||
private TrackRecordingService service;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void preSetUp() {
|
|
||||||
// Prepare looper for Android's message queue
|
|
||||||
if (Looper.myLooper() == null) Looper.prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void finalTearDown() {
|
|
||||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TrackRecordingService startService() throws TimeoutException {
|
|
||||||
Intent startIntent = new Intent(context, TrackRecordingService.class);
|
|
||||||
return ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
|
||||||
.getService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws TimeoutException {
|
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
service = startService();
|
|
||||||
tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
// Ensure that the database is empty after every test
|
|
||||||
contentProviderUtils.deleteAllTracks(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void notRecording_testInsertMarker() {
|
|
||||||
// given
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
|
|
||||||
// when
|
|
||||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNull(markerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void recording_noGPSfix_cannotCreateMarker() {
|
|
||||||
// given
|
|
||||||
service.startNewTrack();
|
|
||||||
|
|
||||||
// when
|
|
||||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNull(markerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void recording_GPSfix_createsMarker() {
|
|
||||||
// given
|
|
||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
|
||||||
Track.Id trackId = service.startNewTrack();
|
|
||||||
service.stopUpdateRecordingData();
|
|
||||||
|
|
||||||
assertTrue(service.isRecording());
|
|
||||||
trackPointCreator.onNewTrackPoint(
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:03Z"))
|
|
||||||
.setLatitude(10)
|
|
||||||
.setLongitude(10)
|
|
||||||
);
|
|
||||||
|
|
||||||
// when
|
|
||||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNotEquals(new Marker.Id(-1L), markerId);
|
|
||||||
Marker wpt = contentProviderUtils.getMarker(markerId);
|
|
||||||
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
|
||||||
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
|
||||||
assertEquals(trackId, wpt.getTrackId());
|
|
||||||
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
|
||||||
assertNotNull(wpt.getLocation());
|
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
|
||||||
service.endCurrentTrack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -314,7 +314,7 @@ limitations under the License.
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.markers.MarkerDetailActivity"
|
android:name=".publicapi.ShowMarkerActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="de.dennisguse.opentracks.MarkerDetails" />
|
<action android:name="de.dennisguse.opentracks.MarkerDetails" />
|
||||||
@@ -322,6 +322,16 @@ limitations under the License.
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".publicapi.CreateMarkerActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="de.dennisguse.opentracks.CreateMarker" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".ui.markers.MarkerDetailActivity" />
|
||||||
<activity android:name=".ui.markers.MarkerEditActivity" />
|
<activity android:name=".ui.markers.MarkerEditActivity" />
|
||||||
<activity android:name=".ui.markers.MarkerListActivity" />
|
<activity android:name=".ui.markers.MarkerListActivity" />
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import de.dennisguse.opentracks.data.ContentProviderUtils;
|
|||||||
import de.dennisguse.opentracks.data.TrackDataHub;
|
import de.dennisguse.opentracks.data.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.databinding.TrackRecordingBinding;
|
import de.dennisguse.opentracks.databinding.TrackRecordingBinding;
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
|
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
|
||||||
@@ -269,9 +270,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (item.getItemId() == R.id.track_detail_insert_marker) {
|
if (item.getItemId() == R.id.track_detail_insert_marker) {
|
||||||
|
TrackPoint trackPoint = trackRecordingServiceConnection.getTrackRecordingService().getLastStoredTrackPointWithLocation();
|
||||||
|
if (trackPoint == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Intent intent = IntentUtils
|
Intent intent = IntentUtils
|
||||||
.newIntent(this, MarkerEditActivity.class)
|
.newIntent(this, MarkerEditActivity.class)
|
||||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId);
|
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_LOCATION, trackPoint.getLocation());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,31 +152,6 @@ public class ChartView extends View {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onSingleTapConfirmed(MotionEvent event) {
|
|
||||||
// Check if the y event is within markerHeight of the marker center
|
|
||||||
if (Math.abs(event.getY() - topBorder - spacer - markerHeight / 2f) < markerHeight) {
|
|
||||||
int minDistance = Integer.MAX_VALUE;
|
|
||||||
Marker nearestMarker = null;
|
|
||||||
synchronized (markers) {
|
|
||||||
for (Marker marker : markers) {
|
|
||||||
int distance = Math.abs(getX(getMarkerXValue(marker)) - (int) event.getX() - getScrollX());
|
|
||||||
if (distance < minDistance) {
|
|
||||||
minDistance = distance;
|
|
||||||
nearestMarker = marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nearestMarker != null && minDistance < markerWidth) {
|
|
||||||
Intent intent = IntentUtils.newIntent(getContext(), MarkerDetailActivity.class)
|
|
||||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, nearestMarker.getId());
|
|
||||||
getContext().startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private final ScaleGestureDetector detectorZoom = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
private final ScaleGestureDetector detectorZoom = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||||
@@ -589,7 +564,6 @@ public class ChartView extends View {
|
|||||||
|
|
||||||
clipToGraphArea(canvas);
|
clipToGraphArea(canvas);
|
||||||
drawDataSeries(canvas);
|
drawDataSeries(canvas);
|
||||||
drawMarker(canvas);
|
|
||||||
drawGrid(canvas);
|
drawGrid(canvas);
|
||||||
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
@@ -630,30 +604,6 @@ public class ChartView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawMarker(Canvas canvas) {
|
|
||||||
synchronized (markers) {
|
|
||||||
for (Marker marker : markers) {
|
|
||||||
double xValue = getMarkerXValue(marker);
|
|
||||||
double markerIconSizeInXaxisUnits = maxX*markerWidth/effectiveWidth / zoomLevel;
|
|
||||||
if (xValue > maxX + markerIconSizeInXaxisUnits * (1-MARKER_X_ANCHOR)) {
|
|
||||||
continue; // there is no chance that this marker will be visible
|
|
||||||
}
|
|
||||||
canvas.save();
|
|
||||||
float x = getX(getMarkerXValue(marker));
|
|
||||||
canvas.drawLine(x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
|
|
||||||
// if marker is not near the end of the track then draw it normally
|
|
||||||
if (xValue < maxX - markerIconSizeInXaxisUnits*(1-MARKER_X_ANCHOR)) {
|
|
||||||
canvas.translate(x - (markerWidth * MARKER_X_ANCHOR), topBorder + spacer);
|
|
||||||
} else { // marker at the end needs to be drawn mirrored so that it is more visible
|
|
||||||
canvas.translate(x + (markerWidth * MARKER_X_ANCHOR), topBorder + spacer);
|
|
||||||
canvas.scale(-1, 1);
|
|
||||||
}
|
|
||||||
markerPin.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the grid.
|
* Draws the grid.
|
||||||
*
|
*
|
||||||
@@ -1043,14 +993,6 @@ public class ChartView extends View {
|
|||||||
return topBorder + yAxisOffset + (int) ((1 - percentage) * rangeHeight);
|
return topBorder + yAxisOffset + (int) ((1 - percentage) * rangeHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getMarkerXValue(Marker marker) {
|
|
||||||
if (chartByDistance) {
|
|
||||||
return marker.getLength().toKM_Miles(unitSystem);
|
|
||||||
} else {
|
|
||||||
return marker.getDuration().toMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a paint's Rect for a string.
|
* Gets a paint's Rect for a string.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -377,8 +377,6 @@ public class ContentProviderUtils {
|
|||||||
int categoryIndex = cursor.getColumnIndexOrThrow(MarkerColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndexOrThrow(MarkerColumns.CATEGORY);
|
||||||
int iconIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ICON);
|
int iconIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ICON);
|
||||||
int trackIdIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TRACKID);
|
int trackIdIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TRACKID);
|
||||||
int lengthIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LENGTH);
|
|
||||||
int durationIndex = cursor.getColumnIndexOrThrow(MarkerColumns.DURATION);
|
|
||||||
int longitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LONGITUDE);
|
int longitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LONGITUDE);
|
||||||
int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE);
|
int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE);
|
||||||
int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
|
int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
|
||||||
@@ -419,13 +417,6 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(iconIndex)) {
|
if (!cursor.isNull(iconIndex)) {
|
||||||
marker.setIcon(cursor.getString(iconIndex));
|
marker.setIcon(cursor.getString(iconIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(lengthIndex)) {
|
|
||||||
marker.setLength(Distance.of(cursor.getFloat(lengthIndex)));
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(durationIndex)) {
|
|
||||||
marker.setDuration(Duration.ofMillis(cursor.getLong(durationIndex)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cursor.isNull(photoUrlIndex)) {
|
if (!cursor.isNull(photoUrlIndex)) {
|
||||||
marker.setPhotoUrl(cursor.getString(photoUrlIndex));
|
marker.setPhotoUrl(cursor.getString(photoUrlIndex));
|
||||||
}
|
}
|
||||||
@@ -542,9 +533,6 @@ public class ContentProviderUtils {
|
|||||||
values.put(MarkerColumns.CATEGORY, marker.getCategory());
|
values.put(MarkerColumns.CATEGORY, marker.getCategory());
|
||||||
values.put(MarkerColumns.ICON, marker.getIcon());
|
values.put(MarkerColumns.ICON, marker.getIcon());
|
||||||
values.put(MarkerColumns.TRACKID, marker.getTrackId().id());
|
values.put(MarkerColumns.TRACKID, marker.getTrackId().id());
|
||||||
values.put(MarkerColumns.LENGTH, marker.getLength().toM());
|
|
||||||
values.put(MarkerColumns.DURATION, marker.getDuration().toMillis());
|
|
||||||
|
|
||||||
values.put(MarkerColumns.LONGITUDE, (int) (marker.getLongitude() * 1E6));
|
values.put(MarkerColumns.LONGITUDE, (int) (marker.getLongitude() * 1E6));
|
||||||
values.put(MarkerColumns.LATITUDE, (int) (marker.getLatitude() * 1E6));
|
values.put(MarkerColumns.LATITUDE, (int) (marker.getLatitude() * 1E6));
|
||||||
values.put(MarkerColumns.TIME, marker.getTime().toEpochMilli());
|
values.put(MarkerColumns.TIME, marker.getTime().toEpochMilli());
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 37;
|
private static final int DATABASE_VERSION = 38;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
@@ -81,6 +81,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
case 35 -> upgradeFrom34to35(db);
|
case 35 -> upgradeFrom34to35(db);
|
||||||
case 36 -> upgradeFrom35to36(db);
|
case 36 -> upgradeFrom35to36(db);
|
||||||
case 37 -> upgradeFrom36to37(db);
|
case 37 -> upgradeFrom36to37(db);
|
||||||
|
case 38 -> upgradeFrom37to38(db);
|
||||||
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
case 34 -> downgradeFrom35to34(db);
|
case 34 -> downgradeFrom35to34(db);
|
||||||
case 35 -> downgradeFrom36to35(db);
|
case 35 -> downgradeFrom36to35(db);
|
||||||
case 36 -> downgradeFrom37to36(db);
|
case 36 -> downgradeFrom37to36(db);
|
||||||
|
case 37 -> downgradeFrom38to37(db);
|
||||||
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,4 +646,28 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void upgradeFrom37to38(SQLiteDatabase db) {
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
db.execSQL("ALTER TABLE markers RENAME TO markers_old");
|
||||||
|
db.execSQL("CREATE TABLE markers (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, icon TEXT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, bearing FLOAT, photoUrl TEXT, FOREIGN KEY (trackid) REFERENCES tracks(_id) ON UPDATE CASCADE ON DELETE CASCADE)");
|
||||||
|
db.execSQL("INSERT INTO markers SELECT _id, name, description, category, icon, trackid, longitude, latitude, time, elevation, accuracy, bearing, photoUrl FROM markers_old");
|
||||||
|
db.execSQL("DROP TABLE markers_old");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX markers_trackid_index ON markers(trackid)");
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downgradeFrom38to37(SQLiteDatabase db) {
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
db.execSQL("ALTER TABLE markers ADD COLUMN length FLOAT");
|
||||||
|
db.execSQL("ALTER TABLE markers ADD COLUMN duration INTEGER");
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import androidx.annotation.Nullable;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
|
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
|
||||||
*
|
*
|
||||||
@@ -53,10 +51,6 @@ public final class Marker {
|
|||||||
private Altitude altitude;
|
private Altitude altitude;
|
||||||
private Float bearing;
|
private Float bearing;
|
||||||
|
|
||||||
//TODO It is the distance from the track starting point; rename to something more meaningful
|
|
||||||
private Distance length;
|
|
||||||
private Duration duration;
|
|
||||||
|
|
||||||
@Deprecated //TODO Make an URI instead of String
|
@Deprecated //TODO Make an URI instead of String
|
||||||
private String photoUrl = "";
|
private String photoUrl = "";
|
||||||
|
|
||||||
@@ -65,7 +59,6 @@ public final class Marker {
|
|||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated //TODO Marker cannot be created without length AND duration!
|
|
||||||
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
|
|
||||||
@@ -75,20 +68,15 @@ public final class Marker {
|
|||||||
throw new RuntimeException("Marker requires a trackpoint with a location.");
|
throw new RuntimeException("Marker requires a trackpoint with a location.");
|
||||||
|
|
||||||
setTrackPoint(trackPoint);
|
setTrackPoint(trackPoint);
|
||||||
|
|
||||||
this.length = Distance.of(0); //TODO Not cool!
|
|
||||||
this.duration = Duration.ofMillis(0); //TODO Not cool!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackStatistics statistics, @NonNull TrackPoint trackPoint, String photoUrl) {
|
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackPoint trackPoint, String photoUrl) {
|
||||||
this(trackId, trackPoint);
|
this(trackId, trackPoint);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.length = statistics.getTotalDistance();
|
|
||||||
this.duration = statistics.getTotalTime();
|
|
||||||
this.photoUrl = photoUrl;
|
this.photoUrl = photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,22 +222,6 @@ public final class Marker {
|
|||||||
this.bearing = bearing;
|
this.bearing = bearing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Distance getLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLength(Distance length) {
|
|
||||||
this.length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Duration getDuration() {
|
|
||||||
return duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDuration(@NonNull Duration duration) {
|
|
||||||
this.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPhotoUrl() {
|
public String getPhotoUrl() {
|
||||||
return photoUrl;
|
return photoUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ public interface MarkerColumns extends BaseColumns {
|
|||||||
String CATEGORY = "category"; // marker category
|
String CATEGORY = "category"; // marker category
|
||||||
String ICON = "icon"; // marker icon
|
String ICON = "icon"; // marker icon
|
||||||
String TRACKID = "trackid"; // track id
|
String TRACKID = "trackid"; // track id
|
||||||
|
|
||||||
String LENGTH = "length"; // length of the track (without smoothing)
|
|
||||||
String DURATION = "duration"; // total duration of the track from the beginning until now
|
|
||||||
|
|
||||||
String LONGITUDE = "longitude"; // longitude
|
String LONGITUDE = "longitude"; // longitude
|
||||||
String LATITUDE = "latitude"; // latitude
|
String LATITUDE = "latitude"; // latitude
|
||||||
String TIME = "time"; // time
|
String TIME = "time"; // time
|
||||||
@@ -61,8 +57,6 @@ public interface MarkerColumns extends BaseColumns {
|
|||||||
+ CATEGORY + " TEXT, "
|
+ CATEGORY + " TEXT, "
|
||||||
+ ICON + " TEXT, "
|
+ ICON + " TEXT, "
|
||||||
+ TRACKID + " INTEGER NOT NULL, "
|
+ TRACKID + " INTEGER NOT NULL, "
|
||||||
+ LENGTH + " FLOAT, "
|
|
||||||
+ DURATION + " INTEGER, "
|
|
||||||
+ LONGITUDE + " INTEGER, "
|
+ LONGITUDE + " INTEGER, "
|
||||||
+ LATITUDE + " INTEGER, "
|
+ LATITUDE + " INTEGER, "
|
||||||
+ TIME + " INTEGER, "
|
+ TIME + " INTEGER, "
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import java.util.Collections;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
@@ -26,7 +25,6 @@ import de.dennisguse.opentracks.data.models.Marker;
|
|||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
@@ -158,7 +156,7 @@ public class TrackImporter {
|
|||||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, trackId);
|
contentProviderUtils.bulkInsertTrackPoint(trackPoints, trackId);
|
||||||
|
|
||||||
// Store Markers
|
// Store Markers
|
||||||
matchMarkers2TrackPoints(trackId);
|
updateMarkers(trackId);
|
||||||
for (Marker marker : markers)
|
for (Marker marker : markers)
|
||||||
marker.setTrackId(trackId); //TODO Should happen in bulkInsertMarkers
|
marker.setTrackId(trackId); //TODO Should happen in bulkInsertMarkers
|
||||||
|
|
||||||
@@ -218,55 +216,16 @@ public class TrackImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: Modifies content of markers (incl. removal).
|
* NOTE: Modifies content of markers.
|
||||||
*/
|
*/
|
||||||
private void matchMarkers2TrackPoints(Track.Id trackId) {
|
private void updateMarkers(Track.Id trackId) {
|
||||||
List<TrackPoint> trackPointsWithLocation = trackPoints.stream()
|
markers.forEach(marker -> {
|
||||||
.filter(TrackPoint::hasLocation)
|
if (marker.hasPhoto()) {
|
||||||
.collect(Collectors.toList());
|
marker.setPhotoUrl(getInternalPhotoUrl(trackId, marker.getPhotoUrl()));
|
||||||
|
|
||||||
List<Marker> todoMarkers = new LinkedList<>(markers);
|
|
||||||
List<Marker> doneMarkers = new LinkedList<>();
|
|
||||||
|
|
||||||
for (final TrackPoint trackPoint : trackPointsWithLocation) {
|
|
||||||
if (todoMarkers.isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
marker.setIcon(context.getString(R.string.marker_icon_url)); //TODO Why?
|
||||||
updater.addTrackPoint(trackPoint);
|
});
|
||||||
|
|
||||||
List<Marker> matchedMarkers = todoMarkers.stream()
|
|
||||||
.filter(it -> trackPoint.getLatitude() == it.getLatitude()
|
|
||||||
&& trackPoint.getLongitude() == it.getLongitude()
|
|
||||||
&& trackPoint.getTime().equals(it.getTime())
|
|
||||||
)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
TrackStatistics statistics = updater.getTrackStatistics();
|
|
||||||
for (Marker marker : matchedMarkers) {
|
|
||||||
if (marker.hasPhoto()) {
|
|
||||||
marker.setPhotoUrl(getInternalPhotoUrl(trackId, marker.getPhotoUrl()));
|
|
||||||
}
|
|
||||||
|
|
||||||
marker.setIcon(context.getString(R.string.marker_icon_url)); //TODO Why?
|
|
||||||
|
|
||||||
marker.setLength(statistics.getTotalDistance());
|
|
||||||
marker.setDuration(statistics.getTotalTime());
|
|
||||||
|
|
||||||
marker.setTrackPoint(trackPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
todoMarkers.removeAll(matchedMarkers);
|
|
||||||
doneMarkers.addAll(matchedMarkers);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (todoMarkers.isEmpty()) {
|
|
||||||
Log.w(TAG, "Some markers could not be attached to TrackPoints; those are not imported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
markers.clear();
|
|
||||||
markers.addAll(doneMarkers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package de.dennisguse.opentracks.publicapi;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
||||||
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public API to creates a Marker for a given track with a given location
|
||||||
|
*/
|
||||||
|
public class CreateMarkerActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
|
public static final String EXTRA_LOCATION = "location";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(getIntent().getLongExtra(EXTRA_TRACK_ID, 0L));
|
||||||
|
Location location = getIntent().getParcelableExtra(EXTRA_LOCATION);
|
||||||
|
|
||||||
|
TrackRecordingServiceConnection.execute(this, (service, self) -> {
|
||||||
|
Intent intent = IntentUtils
|
||||||
|
.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_LOCATION, location);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package de.dennisguse.opentracks.publicapi;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.AbstractActivity;
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.data.models.Marker;
|
||||||
|
import de.dennisguse.opentracks.databinding.MarkerDetailActivityBinding;
|
||||||
|
import de.dennisguse.opentracks.ui.markers.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
|
import de.dennisguse.opentracks.ui.markers.MarkerDetailActivity;
|
||||||
|
import de.dennisguse.opentracks.ui.markers.MarkerDetailFragment;
|
||||||
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public api to show an existing marker
|
||||||
|
*/
|
||||||
|
public class ShowMarkerActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||||
|
|
||||||
|
private static final String TAG = ShowMarkerActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private MarkerDetailActivityBinding viewBinding;
|
||||||
|
|
||||||
|
private List<Marker.Id> markerIds;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle bundle) {
|
||||||
|
super.onCreate(bundle);
|
||||||
|
|
||||||
|
Marker.Id markerId = new Marker.Id(getIntent().getLongExtra(EXTRA_MARKER_ID, 0));
|
||||||
|
if (markerId.id() == 0) {
|
||||||
|
throw new IllegalStateException("No valid markerId provided");
|
||||||
|
}
|
||||||
|
Intent intent = IntentUtils.newIntent(this, MarkerDetailActivity.class)
|
||||||
|
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, markerId);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package de.dennisguse.opentracks.services;
|
package de.dennisguse.opentracks.services;
|
||||||
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
@@ -19,7 +17,6 @@ import de.dennisguse.opentracks.R;
|
|||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
@@ -138,31 +135,6 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|||||||
return new Pair<>(track, current);
|
return new Pair<>(track, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marker.Id insertMarker(String name, String category, String description, String photoUrl) {
|
|
||||||
if (name == null) {
|
|
||||||
Integer nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(trackId);
|
|
||||||
if (nextMarkerNumber == null) {
|
|
||||||
nextMarkerNumber = 1;
|
|
||||||
}
|
|
||||||
name = context.getString(R.string.marker_name_format, nextMarkerNumber + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastStoredTrackPointWithLocation == null) {
|
|
||||||
Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
category = category != null ? category : "";
|
|
||||||
description = description != null ? description : "";
|
|
||||||
String icon = context.getString(R.string.marker_icon_url);
|
|
||||||
photoUrl = photoUrl != null ? photoUrl : "";
|
|
||||||
|
|
||||||
// Insert marker
|
|
||||||
Marker marker = new Marker(name, description, category, icon, trackId, getTrackStatistics(), lastStoredTrackPointWithLocation, photoUrl);
|
|
||||||
Uri uri = contentProviderUtils.insertMarker(marker);
|
|
||||||
return new Marker.Id(ContentUris.parseId(uri));
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
public void onIdle() {
|
public void onIdle() {
|
||||||
Log.d(TAG, "Becoming idle");
|
Log.d(TAG, "Becoming idle");
|
||||||
@@ -247,7 +219,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|||||||
}
|
}
|
||||||
|
|
||||||
TrackStatistics getTrackStatistics() {
|
TrackStatistics getTrackStatistics() {
|
||||||
return trackStatisticsUpdater.getTrackStatistics();
|
return trackStatisticsUpdater == null ? null : trackStatisticsUpdater.getTrackStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertTrackPoint(@NonNull TrackPoint trackPoint, boolean storeLastTrackPointIfUseful) {
|
private void insertTrackPoint(@NonNull TrackPoint trackPoint, boolean storeLastTrackPointIfUseful) {
|
||||||
@@ -308,6 +280,10 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TrackPoint getLastStoredTrackPointWithLocation() {
|
||||||
|
return lastStoredTrackPointWithLocation;
|
||||||
|
}
|
||||||
|
|
||||||
public interface IdleObserver {
|
public interface IdleObserver {
|
||||||
void onIdle();
|
void onIdle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import java.io.StringWriter;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
@@ -57,6 +56,10 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
public static final RecordingData NOT_RECORDING = new RecordingData(null, null, null);
|
public static final RecordingData NOT_RECORDING = new RecordingData(null, null, null);
|
||||||
public static final GpsStatusValue STATUS_GPS_DEFAULT = GpsStatusValue.GPS_NONE;
|
public static final GpsStatusValue STATUS_GPS_DEFAULT = GpsStatusValue.GPS_NONE;
|
||||||
|
|
||||||
|
public TrackPoint getLastStoredTrackPointWithLocation() {
|
||||||
|
return trackRecordingManager.getLastStoredTrackPointWithLocation();
|
||||||
|
}
|
||||||
|
|
||||||
public class Binder extends android.os.Binder {
|
public class Binder extends android.os.Binder {
|
||||||
|
|
||||||
private Binder() {
|
private Binder() {
|
||||||
@@ -269,14 +272,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
gpsStatusObservable.postValue(gpsStatusValue);
|
gpsStatusObservable.postValue(gpsStatusValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marker.Id insertMarker(String name, String category, String description, String photoUrl) {
|
|
||||||
if (!isRecording()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackRecordingManager.insertMarker(name, category, description, photoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public TrackPointCreator getTrackPointCreator() {
|
public TrackPointCreator getTrackPointCreator() {
|
||||||
|
|||||||
@@ -59,15 +59,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
|||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
Object bundleMarkerId = getIntent().getExtras().get(EXTRA_MARKER_ID);
|
Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
||||||
Marker.Id markerId = null;
|
|
||||||
if (bundleMarkerId instanceof Marker.Id cast) {
|
|
||||||
markerId = cast;
|
|
||||||
}
|
|
||||||
if (bundleMarkerId instanceof Long cast) {
|
|
||||||
//Incoming Intent via Dashboard API.
|
|
||||||
markerId = new Marker.Id(cast);
|
|
||||||
}
|
|
||||||
if (markerId == null) {
|
if (markerId == null) {
|
||||||
Log.d(TAG, "invalid marker id");
|
Log.d(TAG, "invalid marker id");
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
@@ -36,19 +37,19 @@ import androidx.activity.result.ActivityResultLauncher;
|
|||||||
import androidx.activity.result.PickVisualMediaRequest;
|
import androidx.activity.result.PickVisualMediaRequest;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.AbstractActivity;
|
import de.dennisguse.opentracks.AbstractActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
import de.dennisguse.opentracks.data.models.Marker;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.databinding.MarkerEditBinding;
|
import de.dennisguse.opentracks.databinding.MarkerEditBinding;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity to add/edit a marker.
|
* An activity to add/edit a marker.
|
||||||
@@ -59,13 +60,14 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||||
|
public static final String EXTRA_LOCATION = "location";
|
||||||
|
|
||||||
private static final String CAMERA_PHOTO_URI_KEY = "camera_photo_uri_key";
|
private static final String CAMERA_PHOTO_URI_KEY = "camera_photo_uri_key";
|
||||||
|
|
||||||
private static final String NEW_MARKER_ID = "new_marker_id";
|
|
||||||
|
|
||||||
private static final String TAG = MarkerEditActivity.class.getSimpleName();
|
private static final String TAG = MarkerEditActivity.class.getSimpleName();
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
private Location location;
|
||||||
|
private Marker.Id markerId;
|
||||||
private Marker marker;
|
private Marker marker;
|
||||||
|
|
||||||
private MenuItem insertPhotoMenuItem;
|
private MenuItem insertPhotoMenuItem;
|
||||||
@@ -93,16 +95,16 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
|
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
|
||||||
@Nullable Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
location = getIntent().getParcelableExtra(EXTRA_LOCATION);
|
||||||
final boolean isNewMarker = markerId == null;
|
markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
||||||
|
if ((trackId == null || location == null) && markerId == null) {
|
||||||
|
throw new IllegalStateException("TrackId and Location must be provided or an existing markerId");
|
||||||
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
cameraPhotoUri = Uri.parse(savedInstanceState.getString(CAMERA_PHOTO_URI_KEY, ""));
|
cameraPhotoUri = Uri.parse(savedInstanceState.getString(CAMERA_PHOTO_URI_KEY, ""));
|
||||||
Marker.Id newMarkerId = savedInstanceState.getParcelable(NEW_MARKER_ID);
|
|
||||||
if (newMarkerId != null) {
|
|
||||||
markerId = newMarkerId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
boolean isNewMarker = markerId == null;
|
||||||
|
|
||||||
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
||||||
|
|
||||||
@@ -157,16 +159,8 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isNewMarker) {
|
||||||
if (markerId == null) {
|
createNewMarker().observe(this, this::loadMarkerData);
|
||||||
TrackRecordingServiceConnection.execute(this, (service, self) -> {
|
|
||||||
Marker.Id newMarkerId = createNewMarker(service);
|
|
||||||
if (newMarkerId == null) {
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
loadMarkerData(newMarkerId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
loadMarkerData(markerId);
|
loadMarkerData(markerId);
|
||||||
}
|
}
|
||||||
@@ -174,35 +168,27 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Marker.Id createNewMarker(TrackRecordingService trackRecordingService) {
|
private LiveData<Marker> createNewMarker() {
|
||||||
try {
|
TrackPoint trackPoint = new TrackPoint(location, Instant.now());
|
||||||
Marker.Id marker = trackRecordingService.insertMarker("", "", "", null);
|
return viewModel.createNewMarker(trackId, trackPoint);
|
||||||
if (marker == null) {
|
|
||||||
Toast.makeText(this, R.string.marker_add_error, Toast.LENGTH_LONG).show();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return marker;
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
Log.e(TAG, "Unable to add marker.", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMarkerData(Marker.Id markerId) {
|
private void loadMarkerData(Marker.Id markerId) {
|
||||||
viewModel.getMarkerData(markerId).observe(this, data -> {
|
viewModel.getMarkerData(markerId).observe(this, this::loadMarkerData);
|
||||||
marker = data;
|
}
|
||||||
viewBinding.markerEditName.setText(marker.getName());
|
|
||||||
viewBinding.markerEditMarkerType.setText(marker.getCategory());
|
|
||||||
viewBinding.markerEditDescription.setText(marker.getDescription());
|
|
||||||
if (marker.hasPhoto()) {
|
|
||||||
setMarkerImageView(marker.getPhotoURI());
|
|
||||||
} else {
|
|
||||||
viewBinding.markerEditPhoto.setImageDrawable(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
hideAndShowOptions();
|
private void loadMarkerData(Marker data) {
|
||||||
});
|
marker = data;
|
||||||
|
viewBinding.markerEditName.setText(marker.getName());
|
||||||
|
viewBinding.markerEditMarkerType.setText(marker.getCategory());
|
||||||
|
viewBinding.markerEditDescription.setText(marker.getDescription());
|
||||||
|
if (marker.hasPhoto()) {
|
||||||
|
setMarkerImageView(marker.getPhotoURI());
|
||||||
|
} else {
|
||||||
|
viewBinding.markerEditPhoto.setImageDrawable(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAndShowOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -210,6 +196,8 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
trackId = null;
|
trackId = null;
|
||||||
|
location = null;
|
||||||
|
markerId = null;
|
||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
viewModel = null;
|
viewModel = null;
|
||||||
takePictureFromGallery = null;
|
takePictureFromGallery = null;
|
||||||
@@ -222,8 +210,6 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
if (cameraPhotoUri != null) {
|
if (cameraPhotoUri != null) {
|
||||||
outState.putString(CAMERA_PHOTO_URI_KEY, cameraPhotoUri.toString());
|
outState.putString(CAMERA_PHOTO_URI_KEY, cameraPhotoUri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
outState.putParcelable(NEW_MARKER_ID, marker.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import java.util.NoSuchElementException;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
import de.dennisguse.opentracks.data.models.Marker;
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
public class MarkerEditViewModel extends AndroidViewModel {
|
public class MarkerEditViewModel extends AndroidViewModel {
|
||||||
@@ -122,22 +124,40 @@ public class MarkerEditViewModel extends AndroidViewModel {
|
|||||||
marker.setName(name);
|
marker.setName(name);
|
||||||
marker.setCategory(category);
|
marker.setCategory(category);
|
||||||
marker.setDescription(description);
|
marker.setDescription(description);
|
||||||
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker);
|
if (marker.getId() == null) {
|
||||||
|
new ContentProviderUtils(getApplication()).insertMarker(marker);
|
||||||
|
} else {
|
||||||
|
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker);
|
||||||
|
}
|
||||||
|
|
||||||
if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoURI()))) {
|
if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoURI()))) {
|
||||||
deletePhoto(photoOriginalUri);
|
deletePhoto(photoOriginalUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<Marker> createNewMarker(Track.Id trackId, TrackPoint trackPoint) {
|
||||||
|
Integer nextMarkerNumber = new ContentProviderUtils(getApplication()).getNextMarkerNumber(trackId);
|
||||||
|
if (nextMarkerNumber == null) {
|
||||||
|
nextMarkerNumber = 1;
|
||||||
|
}
|
||||||
|
String name = getApplication().getString(R.string.marker_name_format, nextMarkerNumber + 1);
|
||||||
|
String icon = getApplication().getString(R.string.marker_icon_url);
|
||||||
|
|
||||||
|
Marker marker = new Marker(name, "", "", icon, trackId, trackPoint, "");
|
||||||
|
|
||||||
|
if (markerData == null) {
|
||||||
|
markerData = new MutableLiveData<>();
|
||||||
|
}
|
||||||
|
markerData.postValue(marker);
|
||||||
|
return markerData;
|
||||||
|
}
|
||||||
|
|
||||||
public void onCancel(boolean isNewMarker) {
|
public void onCancel(boolean isNewMarker) {
|
||||||
Marker marker = getMarker();
|
Marker marker = getMarker();
|
||||||
if (isNewMarker) {
|
if (isNewMarker) {
|
||||||
// it's new marker -> clean all photos.
|
// it's new marker -> clean all photos.
|
||||||
deletePhoto(marker);
|
deletePhoto(marker);
|
||||||
deletePhoto(photoOriginalUri);
|
deletePhoto(photoOriginalUri);
|
||||||
|
|
||||||
new ContentProviderUtils(getApplication()).deleteMarker(getApplication(), marker.getId());
|
|
||||||
|
|
||||||
} else if (photoOriginalUri == null || (marker.hasPhoto() && !marker.getPhotoURI().equals(photoOriginalUri))) {
|
} else if (photoOriginalUri == null || (marker.hasPhoto() && !marker.getPhotoURI().equals(photoOriginalUri))) {
|
||||||
// it's an edit marker -> delete photo if it was empty or it was changed (leaving the original in that case).
|
// it's an edit marker -> delete photo if it was empty or it was changed (leaving the original in that case).
|
||||||
deletePhoto(marker);
|
deletePhoto(marker);
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ limitations under the License.
|
|||||||
<string name="import_progress_summary_exists_msg">Already exists</string>
|
<string name="import_progress_summary_exists_msg">Already exists</string>
|
||||||
<string name="import_progress_summary_errors_msg">Errors</string>
|
<string name="import_progress_summary_errors_msg">Errors</string>
|
||||||
<!-- Marker -->
|
<!-- Marker -->
|
||||||
|
<string name="create_marker_error">Unable to create a marker. Try again.</string>
|
||||||
<string name="marker_add_error">Unable to insert a marker. No GPS signal. Try again.</string>
|
<string name="marker_add_error">Unable to insert a marker. No GPS signal. Try again.</string>
|
||||||
<string name="marker_add_success">A marker was inserted</string>
|
<string name="marker_add_success">A marker was inserted</string>
|
||||||
<string name="marker_add_canceled">Canceled, no marker added</string>
|
<string name="marker_add_canceled">Canceled, no marker added</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user