mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Database enforces foreign key constraints incl. cascading deletes. (#480)
* Database enforces foreign key constraints incl. cascading deletes. Fixes #317.
This commit is contained in:
@@ -109,26 +109,21 @@ public class SearchEngineTest {
|
||||
if (hoursAgo >= 0) {
|
||||
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
|
||||
}
|
||||
Marker waypoint = new Marker(location);
|
||||
Marker waypoint = new Marker(trackId, location);
|
||||
waypoint.setName(title);
|
||||
waypoint.setDescription(description);
|
||||
waypoint.setCategory(category);
|
||||
waypoint.setTrackId(trackId);
|
||||
|
||||
Uri uri = providerUtils.insertMarker(waypoint);
|
||||
return new Marker.Id(ContentUris.parseId(uri));
|
||||
}
|
||||
|
||||
private Marker.Id insertWaypoint(String title, String description, String category) {
|
||||
return insertWaypoint(title, description, category, 0.0, -1, null);
|
||||
private Marker.Id insertWaypoint(String title, String description, String category, Track.Id trackId) {
|
||||
return insertWaypoint(title, description, category, 0.0, -1, trackId);
|
||||
}
|
||||
|
||||
private Marker.Id insertWaypoint(String title, double distance) {
|
||||
return insertWaypoint(title, "", "", distance, -1, null);
|
||||
}
|
||||
|
||||
private Marker.Id insertWaypoint(String title, long hoursAgo) {
|
||||
return insertWaypoint(title, "", "", 0.0, hoursAgo, null);
|
||||
private Marker.Id insertWaypoint(String title, double distance, Track.Id trackId) {
|
||||
return insertWaypoint(title, "", "", distance, -1, trackId);
|
||||
}
|
||||
|
||||
private Marker.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) {
|
||||
@@ -170,13 +165,14 @@ public class SearchEngineTest {
|
||||
// - one which will match in title and category
|
||||
// - one which will match in title and description
|
||||
// - one which will match in all fields
|
||||
insertWaypoint("bb", "cc", "dd");
|
||||
Marker.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc");
|
||||
Marker.Id categoryMatchId = insertWaypoint("bb", "cc", "aa");
|
||||
Marker.Id titleMatchId = insertWaypoint("aa", "bb", "cc");
|
||||
Marker.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
|
||||
Marker.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
|
||||
Marker.Id allMatchId = insertWaypoint("aa", "ba", "ca");
|
||||
Track.Id trackId = insertTrack("", "", "");
|
||||
insertWaypoint("bb", "cc", "dd", trackId);
|
||||
Marker.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc", trackId);
|
||||
Marker.Id categoryMatchId = insertWaypoint("bb", "cc", "aa", trackId);
|
||||
Marker.Id titleMatchId = insertWaypoint("aa", "bb", "cc", trackId);
|
||||
Marker.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca", trackId);
|
||||
Marker.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc", trackId);
|
||||
Marker.Id allMatchId = insertWaypoint("aa", "ba", "ca", trackId);
|
||||
|
||||
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||
@@ -193,11 +189,11 @@ public class SearchEngineTest {
|
||||
// - one waypoint which will match by title
|
||||
// - one track which won't match
|
||||
// - one track which will match by title
|
||||
Marker.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
|
||||
insertWaypoint("bb", "cc", "dd");
|
||||
Marker.Id titleWaypointId = insertWaypoint("aa", "bb", "cc");
|
||||
insertTrack("bb", "cc", "dd");
|
||||
Track.Id trackId = insertTrack("aa", "bb", "cc");
|
||||
Marker.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc", trackId);
|
||||
insertWaypoint("bb", "cc", "dd", trackId);
|
||||
Marker.Id titleWaypointId = insertWaypoint("aa", "bb", "cc", trackId);
|
||||
insertTrack("bb", "cc", "dd");
|
||||
|
||||
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||
@@ -212,9 +208,10 @@ public class SearchEngineTest {
|
||||
@Test
|
||||
public void testSearchWaypointDistance() {
|
||||
// All results match text, but they're at difference distances from the user.
|
||||
Marker.Id farFarAwayId = insertWaypoint("aa", 0.3);
|
||||
Marker.Id nearId = insertWaypoint("ab", 0.1);
|
||||
Marker.Id farId = insertWaypoint("ac", 0.2);
|
||||
Track.Id trackId = insertTrack("", "", "");
|
||||
Marker.Id farFarAwayId = insertWaypoint("aa", 0.3, trackId);
|
||||
Marker.Id nearId = insertWaypoint("ab", 0.1, trackId);
|
||||
Marker.Id farId = insertWaypoint("ac", 0.2, trackId);
|
||||
|
||||
SearchQuery query = new SearchQuery("a", HERE, null, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||
@@ -239,9 +236,10 @@ public class SearchEngineTest {
|
||||
@Test
|
||||
public void testSearchWaypointRecent() {
|
||||
// All results match text, but they're were recorded at different times.
|
||||
Marker.Id oldestId = insertWaypoint("aa", 2);
|
||||
Marker.Id recentId = insertWaypoint("ab", 0);
|
||||
Marker.Id oldId = insertWaypoint("ac", 1);
|
||||
Track.Id trackId = insertTrack("", "", "");
|
||||
Marker.Id oldestId = insertWaypoint("aa", 2, trackId);
|
||||
Marker.Id recentId = insertWaypoint("ab", 0, trackId);
|
||||
Marker.Id oldId = insertWaypoint("ac", 1, trackId);
|
||||
|
||||
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||
@@ -266,10 +264,13 @@ public class SearchEngineTest {
|
||||
@Test
|
||||
public void testSearchCurrentTrackWaypoint() {
|
||||
// All results match text, but one of them is in the current track.
|
||||
Marker.Id otherId = insertWaypoint("aa", 1, new Track.Id(456));
|
||||
Marker.Id currentId = insertWaypoint("ab", 1, new Track.Id(123));
|
||||
Track.Id trackId1 = insertTrack("", "", "");
|
||||
Track.Id trackId2 = insertTrack("", "", "");
|
||||
|
||||
SearchQuery query = new SearchQuery("a", null, new Track.Id(123), NOW);
|
||||
Marker.Id otherId = insertWaypoint("aa", 1, trackId2);
|
||||
Marker.Id currentId = insertWaypoint("ab", 1, trackId1);
|
||||
|
||||
SearchQuery query = new SearchQuery("a", null, trackId1, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||
|
||||
// Waypoint in current track should be promoted.
|
||||
@@ -316,7 +317,7 @@ public class SearchEngineTest {
|
||||
ScoredResult result = results.get(i);
|
||||
assertNotNull(errMsg, result.marker);
|
||||
assertNull(errMsg, result.track);
|
||||
assertEquals(errMsg, waypointIds[i], result.marker.getId());
|
||||
assertEquals(errMsg, waypointIds[i].getId(), result.marker.getId().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-26
@@ -212,7 +212,7 @@ public class CustomContentProviderUtilsTest {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
contentProviderUtils.insertMarker(waypoint);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
@@ -287,8 +287,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
||||
|
||||
Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId2));
|
||||
waypoint.setTrackId(trackId1);
|
||||
Marker waypoint = new Marker(trackId1, contentProviderUtils.getLastValidTrackPoint(trackId2));
|
||||
contentProviderUtils.insertMarker(waypoint);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
@@ -463,7 +462,7 @@ public class CustomContentProviderUtilsTest {
|
||||
track.first.setTrackStatistics(statistics);
|
||||
contentProviderUtils.insertTrack(track.first);
|
||||
|
||||
Marker waypoint = new Marker(track.second[0]);
|
||||
Marker waypoint = new Marker(trackId, track.second[0]);
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
contentProviderUtils.insertMarker(waypoint);
|
||||
|
||||
@@ -522,9 +521,8 @@ public class CustomContentProviderUtilsTest {
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
// Insert at first.
|
||||
Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint1.setDescription(TEST_DESC);
|
||||
waypoint1.setTrackId(trackId);
|
||||
contentProviderUtils.insertMarker(waypoint1);
|
||||
|
||||
// Check insert was done.
|
||||
@@ -600,14 +598,12 @@ public class CustomContentProviderUtilsTest {
|
||||
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
||||
|
||||
// Insert at first.
|
||||
Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint1.setDescription(MOCK_DESC);
|
||||
waypoint1.setTrackId(trackId);
|
||||
Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
|
||||
|
||||
Marker waypoint2 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint2.setDescription(MOCK_DESC);
|
||||
waypoint2.setTrackId(trackId);
|
||||
Marker.Id waypoint2Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint2)));
|
||||
|
||||
// Delete
|
||||
@@ -626,14 +622,10 @@ public class CustomContentProviderUtilsTest {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint1.setTrackId(trackId);
|
||||
Marker waypoint2 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint2.setTrackId(trackId);
|
||||
Marker waypoint3 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint3.setTrackId(trackId);
|
||||
Marker waypoint4 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint4.setTrackId(trackId);
|
||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint3 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint4 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
contentProviderUtils.insertMarker(waypoint1);
|
||||
contentProviderUtils.insertMarker(waypoint2);
|
||||
contentProviderUtils.insertMarker(waypoint3);
|
||||
@@ -651,9 +643,8 @@ public class CustomContentProviderUtilsTest {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
||||
|
||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(waypointId).getDescription());
|
||||
@@ -668,9 +659,8 @@ public class CustomContentProviderUtilsTest {
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
// Insert at first.
|
||||
Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
||||
|
||||
// Update
|
||||
@@ -695,7 +685,6 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
||||
|
||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
||||
@@ -731,7 +720,6 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
||||
|
||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
||||
@@ -766,10 +754,8 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
Marker otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||
otherWaypoint.setDescription(TEST_DESC);
|
||||
otherWaypoint.setTrackId(trackId);
|
||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
||||
contentProviderUtils.insertMarker(otherWaypoint);
|
||||
|
||||
|
||||
+18
-35
@@ -6,7 +6,6 @@ import android.database.sqlite.SQLiteConstraintException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
@@ -38,39 +37,21 @@ public class CustomSQLiteOpenHelperTest {
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
/**
|
||||
* Get the SQL create statements for all tables (ordered by name).
|
||||
* Get the SQL create statements for all SQLite elements of type (ordered by name).
|
||||
*
|
||||
* @return Map(TableName, SQL)
|
||||
* @param type index, table
|
||||
* @return Map(name, SQL)
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static Map<String, String> getTableSQL(SQLiteDatabase db) {
|
||||
HashMap<String, String> tableSQL = new HashMap<>();
|
||||
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + MarkerColumns.TABLE_NAME + "')", null, null, null, "name")) {
|
||||
public static Map<String, String> getSQL(SQLiteDatabase db, String type) {
|
||||
HashMap<String, String> sqlMap = new HashMap<>();
|
||||
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "type=?", new String[]{type}, null, null, "name")) {
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
tableSQL.put(cursor.getString(0), cursor.getString(1));
|
||||
sqlMap.put(cursor.getString(0), cursor.getString(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return tableSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL create statements for all indices (ordered by name).
|
||||
*
|
||||
* @return Map(tableName, SQL)
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static Map<String, String> getIndexSQL(SQLiteDatabase db) {
|
||||
HashMap<String, String> indexSQL = new HashMap<>();
|
||||
try (Cursor cursor = db.rawQuery("SELECT tbl_name, SQL FROM sqlite_master WHERE type = 'index'", null)) {
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
indexSQL.put(cursor.getString(0), cursor.getString(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return indexSQL;
|
||||
return sqlMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +90,7 @@ public class CustomSQLiteOpenHelperTest {
|
||||
assertTrue(hasSqlCreate(db, MarkerColumns.CREATE_TABLE));
|
||||
assertTrue(hasSqlCreate(db, MarkerColumns.CREATE_TABLE_INDEX));
|
||||
} catch (Exception e) {
|
||||
fail();
|
||||
fail("Database could not be created: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +102,8 @@ public class CustomSQLiteOpenHelperTest {
|
||||
Map<String, String> tableByUpgrade;
|
||||
Map<String, String> indicesByUpgrade;
|
||||
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
||||
tableByUpgrade = getTableSQL(dbUpgraded);
|
||||
indicesByUpgrade = getIndexSQL(dbUpgraded);
|
||||
tableByUpgrade = getSQL(dbUpgraded, "table");
|
||||
indicesByUpgrade = getSQL(dbUpgraded, "index");
|
||||
}
|
||||
context.deleteDatabase(DATABASE_NAME);
|
||||
|
||||
@@ -130,12 +111,14 @@ public class CustomSQLiteOpenHelperTest {
|
||||
Map<String, String> tablesByCreate;
|
||||
Map<String, String> indicesByCreate;
|
||||
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
||||
tablesByCreate = getTableSQL(dbCreated);
|
||||
indicesByCreate = getIndexSQL(dbCreated);
|
||||
tablesByCreate = getSQL(dbCreated, "table");
|
||||
indicesByCreate = getSQL(dbCreated, "index");
|
||||
}
|
||||
|
||||
|
||||
// then - verify table structure
|
||||
assertEquals(3, tableByUpgrade.size());
|
||||
int tableCount = 3 + 2; //Three with data tables + two SQLite
|
||||
assertEquals(tableCount, tableByUpgrade.size());
|
||||
assertEquals(tableByUpgrade.size(), tablesByCreate.size());
|
||||
|
||||
assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME));
|
||||
@@ -158,8 +141,8 @@ public class CustomSQLiteOpenHelperTest {
|
||||
Map<String, String> tablesByDowngrade;
|
||||
Map<String, String> indicesByDowngrade;
|
||||
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
|
||||
tablesByDowngrade = getTableSQL(db);
|
||||
indicesByDowngrade = getIndexSQL(db);
|
||||
tablesByDowngrade = getSQL(db, "table");
|
||||
indicesByDowngrade = getSQL(db, "index");
|
||||
}
|
||||
|
||||
// then - verify table structure
|
||||
|
||||
+4
-3
@@ -72,13 +72,12 @@ public class ExportImportTest {
|
||||
trackPoints.addAll(Arrays.asList(track.second));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Marker marker = new Marker(track.second[i].getLocation());
|
||||
Marker marker = new Marker(trackId, track.second[i].getLocation());
|
||||
marker.setName("the marker " + i);
|
||||
marker.setDescription("the marker description " + i);
|
||||
marker.setCategory("the marker category" + i);
|
||||
marker.setIcon("the marker icon" + i);
|
||||
marker.setPhotoUrl("the photo url" + i);
|
||||
marker.setTrackId(trackId);
|
||||
contentProviderUtils.insertMarker(marker);
|
||||
|
||||
markers.add(marker);
|
||||
@@ -90,7 +89,9 @@ public class ExportImportTest {
|
||||
@After
|
||||
public void tearDown() {
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||
if (importTrackId != null) {
|
||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
|
||||
@@ -40,20 +40,28 @@ public final class Marker {
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String icon = "";
|
||||
private Track.Id trackId;
|
||||
private final Track.Id trackId;
|
||||
//TODO It is the distance from the track starting point; rename to something more meaningful
|
||||
private double length = 0.0;
|
||||
private long duration = 0;
|
||||
private Location location;
|
||||
private final Location location;
|
||||
@Deprecated //TODO Make an URI instead of String
|
||||
private String photoUrl = "";
|
||||
|
||||
@VisibleForTesting
|
||||
public Marker(@NonNull TrackPoint trackPoint) {
|
||||
this.location = trackPoint.getLocation();
|
||||
public Marker(@NonNull Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||
this(trackId, trackPoint.getLocation());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
//TODO Used by AbstractFileImporter to create an intermediate marker before saving a new one into the database.
|
||||
public Marker(@NonNull Location location) {
|
||||
this(null, location);
|
||||
}
|
||||
|
||||
public Marker(@NonNull Track.Id trackId, @NonNull Location location) {
|
||||
this.location = location;
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, double length, long duration, @NonNull Location location, String photoUrl) {
|
||||
@@ -112,14 +120,11 @@ public final class Marker {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public Track.Id getTrackId() {
|
||||
public @NonNull
|
||||
Track.Id getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
public void setTrackId(Track.Id trackId) {
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
*/
|
||||
public interface MarkerColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "waypoints";
|
||||
String TABLE_NAME = "markers";
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
||||
@@ -60,7 +60,7 @@ public interface MarkerColumns extends BaseColumns {
|
||||
+ DESCRIPTION + " TEXT, "
|
||||
+ CATEGORY + " TEXT, "
|
||||
+ ICON + " TEXT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ TRACKID + " INTEGER NOT NULL, "
|
||||
+ LENGTH + " FLOAT, "
|
||||
+ DURATION + " INTEGER, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
@@ -69,7 +69,8 @@ public interface MarkerColumns extends BaseColumns {
|
||||
+ ALTITUDE + " FLOAT, "
|
||||
+ ACCURACY + " FLOAT, "
|
||||
+ BEARING + " FLOAT, "
|
||||
+ PHOTOURL + " TEXT"
|
||||
+ PHOTOURL + " TEXT, "
|
||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
String CREATE_TABLE_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + TRACKID + "_index ON " + TABLE_NAME + "(" + TRACKID + ")";
|
||||
|
||||
@@ -40,7 +40,9 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
String LONGITUDE = "longitude";
|
||||
String LATITUDE = "latitude";
|
||||
@Deprecated
|
||||
double PAUSE_LATITUDE = 100.0;
|
||||
@Deprecated
|
||||
double RESUME_LATITUDE = 200.0;
|
||||
|
||||
String TIME = "time";
|
||||
@@ -55,7 +57,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ TRACKID + " INTEGER NOT NULL, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
+ LATITUDE + " INTEGER, "
|
||||
+ TIME + " INTEGER, "
|
||||
@@ -66,7 +68,9 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
+ SENSOR_HEARTRATE + " FLOAT, "
|
||||
+ SENSOR_CADENCE + " FLOAT, "
|
||||
+ SENSOR_POWER + " FLOAT, "
|
||||
+ ELEVATION_GAIN + " FLOAT)";
|
||||
+ ELEVATION_GAIN + " FLOAT, "
|
||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
String CREATE_TABLE_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + TRACKID + "_index ON " + TABLE_NAME + "(" + TRACKID + ")";
|
||||
}
|
||||
|
||||
@@ -179,17 +179,7 @@ public class ContentProviderUtils {
|
||||
FileUtils.deleteDirectoryRecurse(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a track.
|
||||
*
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public void deleteTrack(Context context, Track.Id trackId) {
|
||||
if (trackId == null) {
|
||||
return;
|
||||
}
|
||||
deleteTrackPointsAndMarkers(trackId);
|
||||
|
||||
public void deleteTrack(Context context, @NonNull Track.Id trackId) {
|
||||
// Delete track folder resources.
|
||||
FileUtils.deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId));
|
||||
|
||||
@@ -363,7 +353,8 @@ public class ContentProviderUtils {
|
||||
location.setBearing(cursor.getFloat(bearingIndex));
|
||||
}
|
||||
|
||||
Marker marker = new Marker(location);
|
||||
Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex));
|
||||
Marker marker = new Marker(trackId, location);
|
||||
|
||||
if (!cursor.isNull(idIndex)) {
|
||||
marker.setId(new Marker.Id(cursor.getLong(idIndex)));
|
||||
@@ -380,9 +371,6 @@ public class ContentProviderUtils {
|
||||
if (!cursor.isNull(iconIndex)) {
|
||||
marker.setIcon(cursor.getString(iconIndex));
|
||||
}
|
||||
if (!cursor.isNull(trackIdIndex)) {
|
||||
marker.setTrackId(new Track.Id(cursor.getLong(trackIdIndex)));
|
||||
}
|
||||
if (!cursor.isNull(lengthIndex)) {
|
||||
marker.setLength(cursor.getFloat(lengthIndex));
|
||||
}
|
||||
@@ -561,9 +549,7 @@ public class ContentProviderUtils {
|
||||
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
|
||||
values.put(MarkerColumns.CATEGORY, marker.getCategory());
|
||||
values.put(MarkerColumns.ICON, marker.getIcon());
|
||||
if (marker.getTrackId() != null) {
|
||||
values.put(MarkerColumns.TRACKID, marker.getTrackId().getId());
|
||||
}
|
||||
values.put(MarkerColumns.TRACKID, marker.getTrackId().getId());
|
||||
values.put(MarkerColumns.LENGTH, marker.getLength());
|
||||
values.put(MarkerColumns.DURATION, marker.getDuration());
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||
|
||||
/**
|
||||
* A {@link ContentProvider} that handles access to track points, tracks, and markers tables.
|
||||
* <p>
|
||||
* Data consistency is enforced using Foreign Key Constraints within the database incl. cascading deletes.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
@@ -82,6 +84,8 @@ public class CustomContentProvider extends ContentProvider {
|
||||
CustomSQLiteOpenHelper databaseHelper = new CustomSQLiteOpenHelper(context);
|
||||
try {
|
||||
db = databaseHelper.getWritableDatabase();
|
||||
// Necessary to enable cascade deletion from Track to TrackPoints and Markers
|
||||
db.setForeignKeyConstraintsEnabled(true);
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Unable to open database for writing.", e);
|
||||
}
|
||||
|
||||
+66
-2
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private static final int DATABASE_VERSION = 27;
|
||||
private static final int DATABASE_VERSION = 28;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String DATABASE_NAME = "database.db";
|
||||
@@ -73,7 +73,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 27:
|
||||
upgradeFrom26to27(db);
|
||||
break;
|
||||
|
||||
case 28:
|
||||
upgradeFrom27to28(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||
@@ -98,6 +100,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 26:
|
||||
downgradeFrom27to26(db);
|
||||
break;
|
||||
case 27:
|
||||
downgradeFrom28to27(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||
@@ -142,6 +147,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add indeces for foreign key trackId
|
||||
*/
|
||||
private void upgradeFrom24to25(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
@@ -162,6 +170,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add track UUID to prevent re-import of existing tracks
|
||||
*/
|
||||
private void upgradeFrom25to26(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
@@ -198,6 +209,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add elevation gain
|
||||
*/
|
||||
private void upgradeFrom26to27(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
@@ -220,4 +234,54 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add foreign key constraints on trackId
|
||||
*/
|
||||
private void upgradeFrom27to28(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints
|
||||
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
|
||||
db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT, elevation_gain FLOAT, FOREIGN KEY (trackid) REFERENCES tracks(_id) ON UPDATE CASCADE ON DELETE CASCADE)");
|
||||
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power, elevation_gain FROM trackpoints_old");
|
||||
db.execSQL("DROP TABLE trackpoints_old");
|
||||
|
||||
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
|
||||
|
||||
// Markers
|
||||
db.execSQL("ALTER TABLE waypoints 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, length FLOAT, duration INTEGER, 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, length, duration, 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 downgradeFrom28to27(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints
|
||||
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
|
||||
db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT, elevation_gain FLOAT)");
|
||||
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power, elevation_gain FROM trackpoints_old");
|
||||
db.execSQL("DROP TABLE trackpoints_old");
|
||||
|
||||
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
|
||||
|
||||
// Markers
|
||||
db.execSQL("CREATE TABLE waypoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, icon TEXT, trackid INTEGER, length FLOAT, duration INTEGER, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, bearing FLOAT, photoUrl TEXT)");
|
||||
|
||||
db.execSQL("INSERT INTO waypoints SELECT _id, name, description, category, icon, trackid, length, duration, longitude, latitude, time, elevation, accuracy, bearing, photoUrl FROM markers");
|
||||
db.execSQL("DROP TABLE markers");
|
||||
|
||||
db.execSQL("CREATE INDEX waypoints_trackid_index ON waypoints(trackid)");
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user