Implement Track.Id and Waypoint.Id (with tests).

Fixes #276.
This commit is contained in:
Dennis Guse
2020-08-19 18:10:13 +02:00
parent bcbaf4985a
commit 0ab14e0f3d
20 changed files with 124 additions and 58 deletions
@@ -280,7 +280,12 @@ public class SearchEngineTest {
} }
private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) { private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) {
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results; long[] longTrackIds = new long[trackIds.length];
for (int i = 0; i < trackIds.length; i++) {
longTrackIds[i] = trackIds[i].getId();
}
String errMsg = "Expected IDs=" + Arrays.toString(longTrackIds) + "; results=" + results;
Assert.assertEquals(results.size(), trackIds.length); Assert.assertEquals(results.size(), trackIds.length);
for (int i = 0; i < results.size(); i++) { for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i); ScoredResult result = results.get(i);
@@ -297,7 +302,12 @@ public class SearchEngineTest {
} }
private void assertWaypointResults(List<ScoredResult> results, Waypoint.Id... waypointIds) { private void assertWaypointResults(List<ScoredResult> results, Waypoint.Id... waypointIds) {
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results; long[] longWaypointIds = new long[waypointIds.length];
for (int i = 0; i < waypointIds.length; i++) {
longWaypointIds[i] = waypointIds[i].getId();
}
String errMsg = "Expected IDs=" + Arrays.toString(longWaypointIds) + "; results=" + results;
Assert.assertEquals(results.size(), waypointIds.length); Assert.assertEquals(results.size(), waypointIds.length);
for (int i = 0; i < results.size(); i++) { for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i); ScoredResult result = results.get(i);
@@ -23,7 +23,7 @@ public class TestDataUtil {
public static Track createTrack(Track.Id trackId) { public static Track createTrack(Track.Id trackId) {
Track track = new Track(); Track track = new Track();
track.setId(trackId); track.setId(trackId);
track.setName("Test: " + trackId); track.setName("Test: " + trackId.getId());
return track; return track;
} }
@@ -135,7 +135,7 @@ public class CustomContentProviderUtilsTest {
private long initializeTrack(Track.Id id, int numPoints) { private long initializeTrack(Track.Id id, int numPoints) {
Track track = new Track(); Track track = new Track();
track.setId(id); track.setId(id);
track.setName("Test: " + id); track.setName("Test: " + id.getId());
contentProviderUtils.insertTrack(track); contentProviderUtils.insertTrack(track);
track = contentProviderUtils.getTrack(id); track = contentProviderUtils.getTrack(id);
Assert.assertNotNull(track); Assert.assertNotNull(track);
@@ -173,13 +173,13 @@ public class CustomContentProviderUtilsTest {
*/ */
@Test @Test
public void testCreateTrack() { public void testCreateTrack() {
long trackId = System.currentTimeMillis(); Track.Id trackId = new Track.Id(System.currentTimeMillis());
int columnIndex = 1; int columnIndex = 1;
// Id // Id
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex); when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false); when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getLong(columnIndex)).thenReturn(trackId); when(cursorMock.getLong(columnIndex)).thenReturn(trackId.getId());
//Uuid //Uuid
columnIndex++; columnIndex++;
@@ -189,7 +189,7 @@ public class CustomContentProviderUtilsTest {
// Name // Name
columnIndex++; columnIndex++;
String name = NAME_PREFIX + trackId; String name = NAME_PREFIX + trackId.getId();
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex); when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false); when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getString(columnIndex)).thenReturn(name); when(cursorMock.getString(columnIndex)).thenReturn(name);
@@ -468,7 +468,7 @@ public class CustomContentProviderUtilsTest {
Waypoint.Id waypointId = new Waypoint.Id(System.currentTimeMillis()); Waypoint.Id waypointId = new Waypoint.Id(System.currentTimeMillis());
waypoint.setId(waypointId); waypoint.setId(waypointId);
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint); ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID)); Assert.assertEquals(waypointId.getId(), contentValues.get(WaypointsColumns._ID));
Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE)); Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE));
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION)); Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
} }
@@ -694,7 +694,7 @@ public class CustomContentProviderUtilsTest {
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); Assert.assertEquals(1, dir.list().length);
@@ -730,7 +730,7 @@ public class CustomContentProviderUtilsTest {
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); Assert.assertEquals(1, dir.list().length);
@@ -769,7 +769,7 @@ public class CustomContentProviderUtilsTest {
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
contentProviderUtils.insertWaypoint(otherWaypoint); contentProviderUtils.insertWaypoint(otherWaypoint);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); Assert.assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(2, dir.list().length); Assert.assertEquals(2, dir.list().length);
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.location.Location; import android.location.Location;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,6 +34,7 @@ import androidx.test.rule.ServiceTestRule;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -69,6 +71,13 @@ public class TrackRecordingServiceTest {
@Rule @Rule
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION); public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
if (Looper.myLooper() == null) Looper.prepare();
}
private final Context context = ApplicationProvider.getApplicationContext(); private final Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils contentProviderUtils; private ContentProviderUtils contentProviderUtils;
@@ -136,7 +145,7 @@ public class TrackRecordingServiceTest {
// then // then
// Test if we start in no-recording mode by default. // Test if we start in no-recording mode by default.
Assert.assertFalse(service.isRecording()); Assert.assertFalse(service.isRecording());
Assert.assertEquals(-1L, service.getRecordingTrackId()); Assert.assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -150,7 +159,7 @@ public class TrackRecordingServiceTest {
// then // then
Assert.assertFalse(service.isRecording()); Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId()); Assert.assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -234,7 +243,7 @@ public class TrackRecordingServiceTest {
// then // then
Assert.assertFalse(service.isRecording()); Assert.assertFalse(service.isRecording());
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId()); Assert.assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -251,7 +260,7 @@ public class TrackRecordingServiceTest {
Track.Id newTrackId = service.startNewTrack(); Track.Id newTrackId = service.startNewTrack();
// then // then
Assert.assertNotNull(newTrackId); Assert.assertNull(newTrackId);
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context)); Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId()); Assert.assertEquals(trackId, service.getRecordingTrackId());
@@ -270,7 +279,7 @@ public class TrackRecordingServiceTest {
// then // then
Assert.assertFalse(PreferencesUtils.isRecording(context)); Assert.assertFalse(PreferencesUtils.isRecording(context));
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId()); Assert.assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -228,7 +228,7 @@ public class TrackRecordingServiceTestLooper {
// Stop the track. Validate if it has correct data. // Stop the track. Validate if it has correct data.
service.endCurrentTrack(); service.endCurrentTrack();
Assert.assertFalse(service.isRecording()); Assert.assertFalse(service.isRecording());
Assert.assertEquals(-1L, service.getRecordingTrackId()); Assert.assertNull(service.getRecordingTrackId());
track = contentProviderUtils.getTrack(trackId); track = contentProviderUtils.getTrack(trackId);
Assert.assertNotNull(track); Assert.assertNotNull(track);
Assert.assertEquals(trackId, track.getId()); Assert.assertEquals(trackId, track.getId());
@@ -18,6 +18,9 @@ package de.dennisguse.opentracks;
import android.content.Intent; import android.content.Intent;
import java.util.ArrayList;
import java.util.Arrays;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
@@ -76,7 +79,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
getTrackRecordingServiceConnection().stopRecording(this, false); getTrackRecordingServiceConnection().stopRecording(this, false);
} }
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class); Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds); intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
startActivityForResult(intent, DELETE_REQUEST_CODE); startActivityForResult(intent, DELETE_REQUEST_CODE);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
} }
@@ -124,13 +124,13 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
contentProviderUtils = new ContentProviderUtils(this); contentProviderUtils = new ContentProviderUtils(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this); sharedPreferences = PreferencesUtils.getSharedPreferences(this);
track = !trackId.isValid() ? contentProviderUtils.getTrack(trackId) : null; track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
listView = findViewById(R.id.marker_list); listView = findViewById(R.id.marker_list);
listView.setEmptyView(findViewById(R.id.marker_list_empty)); listView.setEmptyView(findViewById(R.id.marker_list_empty));
listView.setOnItemClickListener((parent, view, position, id) -> { listView.setOnItemClickListener((parent, view, position, id) -> {
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class) Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id); .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Waypoint.Id(id));
startActivity(intent); startActivity(intent);
}); });
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) { resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@@ -167,7 +167,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
if (track != null) { if (track != null) {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION, return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.TRACKID + "=?", WaypointsColumns.TRACKID + "=?",
new String[]{String.valueOf(track.getId())}, null); new String[]{String.valueOf(track.getId().getId())}, null);
} else { } else {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION, return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
null, null, null); null, null, null);
@@ -242,8 +242,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
/** /**
* Handles a context item selection. * Handles a context item selection.
* *
* @param itemId the menu item id * @param itemId the menu item id
* @param waypointIds the marker ids * @param longWaypointIds the marker ids
* @return true if handled. * @return true if handled.
*/ */
private boolean handleContextItem(int itemId, long... longWaypointIds) { private boolean handleContextItem(int itemId, long... longWaypointIds) {
@@ -21,6 +21,8 @@ import android.os.Bundle;
import android.os.PowerManager; import android.os.PowerManager;
import android.view.View; import android.view.View;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.SystemUtils; import de.dennisguse.opentracks.util.SystemUtils;
@@ -34,7 +36,7 @@ public class TrackDeleteActivity extends AbstractActivity {
public static final String EXTRA_TRACK_IDS = "track_ids"; public static final String EXTRA_TRACK_IDS = "track_ids";
private Track.Id[] trackIds; private List<Track.Id> trackIds;
private Thread deleteThread; private Thread deleteThread;
@@ -45,7 +47,7 @@ public class TrackDeleteActivity extends AbstractActivity {
setResult(RESULT_CANCELED); setResult(RESULT_CANCELED);
Intent intent = getIntent(); Intent intent = getIntent();
trackIds = (Track.Id[]) intent.getParcelableArrayExtra(EXTRA_TRACK_IDS); trackIds = intent.getParcelableArrayListExtra(EXTRA_TRACK_IDS);
deleteThread = new Thread(() -> { deleteThread = new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
@@ -105,7 +105,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Preferences // Preferences
private boolean metricUnits = true; private boolean metricUnits = true;
private Track.Id recordingTrackId = new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT); private Track.Id recordingTrackId = null;
// Callback when an item is selected in the contextual action mode // Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() { private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@@ -249,11 +249,11 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
if (trackId == recordingTrackId.getId()) { if (trackId == recordingTrackId.getId()) {
// Is recording -> open record activity. // Is recording -> open record activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class) newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId); .putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
} else { } else {
// Not recording -> open detail activity. // Not recording -> open detail activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class) newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId); .putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
} }
startActivity(newIntent); startActivity(newIntent);
}); });
@@ -479,8 +479,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
/** /**
* Handles a context item selection. * Handles a context item selection.
* *
* @param itemId the menu item id * @param itemId the menu item id
* @param trackIds the track ids * @param longTrackIds the track ids
* @return true if handled. * @return true if handled.
*/ */
private boolean handleContextItem(int itemId, long... longTrackIds) { private boolean handleContextItem(int itemId, long... longTrackIds) {
@@ -133,7 +133,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
// Update UI // Update UI
this.invalidateOptionsMenu(); this.invalidateOptionsMenu();
if (!trackId.isValid()) { if (trackId.isValid()) {
trackDataHub.loadTrack(trackId); trackDataHub.loadTrack(trackId);
} }
} }
@@ -251,7 +251,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
trackId = waypoint.getTrackId(); trackId = waypoint.getTrackId();
} }
if (!trackId.isValid()) { if (trackId == null) {
Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID."); Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID.");
finish(); finish();
} }
@@ -167,7 +167,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID); trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID);
} else { } else {
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID); trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
if (contentProviderUtils.getTrack(trackId) == null) { if (trackId != null && contentProviderUtils.getTrack(trackId) == null) {
Log.w(TAG, "TrackId does not exists; cannot continue the recording.");
finish(); finish();
} }
} }
@@ -247,7 +248,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// Update UI // Update UI
this.invalidateOptionsMenu(); this.invalidateOptionsMenu();
if (!trackId.isValid()) { if (trackId != null) {
trackDataHub.loadTrack(trackId); trackDataHub.loadTrack(trackId);
trackController.onResume(true, recordingTrackPaused); trackController.onResume(true, recordingTrackPaused);
} }
@@ -290,7 +290,7 @@ public class SearchEngine {
score *= getTimeBoost(query, location.getTime()); score *= getTimeBoost(query, location.getTime());
// Score waypoints in the currently-selected track higher (searching inside the current track). // Score waypoints in the currently-selected track higher (searching inside the current track).
if (query.currentTrackId.isValid() && waypoint.getTrackId().equals(query.currentTrackId)) { if (waypoint.getTrackId() != null && waypoint.getTrackId().equals(query.currentTrackId)) {
score *= CURRENT_TRACK_WAYPOINT_PROMOTION; score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
} }
@@ -417,8 +417,8 @@ public class SearchEngine {
@Override @Override
public String toString() { public String toString() {
return "ScoredResult [" return "ScoredResult ["
+ (track != null ? ("trackId=" + track.getId() + ", ") : "") + (track != null ? ("trackId=" + track.getId().getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "") + (waypoint != null ? ("wptId=" + waypoint.getId().getId() + ", ") : "")
+ "score=" + score + "]"; + "score=" + score + "]";
} }
} }
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.util.Collections; import java.util.Collections;
@@ -174,10 +175,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
handler = null; handler = null;
} }
public void loadTrack(final Track.Id trackId) { public void loadTrack(final @NonNull Track.Id trackId) {
runInHandlerThread(() -> { runInHandlerThread(() -> {
if (trackId == selectedTrackId) { if (trackId.equals(selectedTrackId)) {
Log.i(TAG, "Not reloading track " + trackId); Log.i(TAG, "Not reloading track " + trackId.getId());
return; return;
} }
selectedTrackId = trackId; selectedTrackId = trackId;
@@ -212,14 +213,14 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* Returns true if the selected track is recording. * Returns true if the selected track is recording.
*/ */
public boolean isSelectedTrackRecording() { public boolean isSelectedTrackRecording() {
return selectedTrackId == recordingTrackId && PreferencesUtils.isRecording(recordingTrackId); return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && PreferencesUtils.isRecording(recordingTrackId);
} }
/** /**
* Returns true if the selected track is paused. * Returns true if the selected track is paused.
*/ */
public boolean isSelectedTrackPaused() { public boolean isSelectedTrackPaused() {
return selectedTrackId == recordingTrackId && recordingTrackPaused; return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
} }
@Override @Override
@@ -356,6 +357,11 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
long localLastSeenTrackPointIdId = updateSamplingState ? lastSeenTrackPointId : -1L; long localLastSeenTrackPointIdId = updateSamplingState ? lastSeenTrackPointId : -1L;
long maxPointId = updateSamplingState ? -1L : lastSeenTrackPointId; long maxPointId = updateSamplingState ? -1L : lastSeenTrackPointId;
if (selectedTrackId == null) {
Log.w(TAG, "This should not happen, but it does"); //TODO
return;
}
long lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId); long lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId);
int samplingFrequency = -1; int samplingFrequency = -1;
boolean includeNextPoint = false; boolean includeNextPoint = false;
@@ -20,6 +20,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@@ -49,7 +50,11 @@ public class Track {
public Track() { public Track() {
} }
public Id getId() { /**
* May be null if the track was not loaded from the database.
*/
public @Nullable
Id getId() {
return id; return id;
} }
@@ -122,6 +127,7 @@ public class Track {
return id; return id;
} }
@Deprecated //TODO Use a Track.Id of null instead
public boolean isValid() { public boolean isValid() {
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
} }
@@ -144,6 +150,12 @@ public class Track {
return 0; return 0;
} }
@NonNull
@Override
public String toString() {
throw new RuntimeException("Not supported");
}
@Override @Override
public void writeToParcel(Parcel parcel, int i) { public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(id); parcel.writeLong(id);
@@ -22,6 +22,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.util.Objects; import java.util.Objects;
@@ -69,7 +70,11 @@ public final class Waypoint {
this.photoUrl = photoUrl; this.photoUrl = photoUrl;
} }
public Id getId() { /**
* May be null if the waypoint was not loaded from the database.
*/
public @Nullable
Id getId() {
return id; return id;
} }
@@ -171,6 +176,7 @@ public final class Waypoint {
return id; return id;
} }
@Deprecated //TODO Use a Waypoint.Id of null instead
public boolean isValid() { public boolean isValid() {
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
} }
@@ -188,6 +194,12 @@ public final class Waypoint {
return Objects.hash(id); return Objects.hash(id);
} }
@NonNull
@Override
public String toString() {
throw new RuntimeException("Not supported");
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@@ -23,6 +23,7 @@ import android.database.Cursor;
import android.location.Location; import android.location.Location;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -186,6 +187,9 @@ public class ContentProviderUtils {
* @param trackId the track id * @param trackId the track id
*/ */
public void deleteTrack(Context context, Track.Id trackId) { public void deleteTrack(Context context, Track.Id trackId) {
if (trackId == null) {
return;
}
deleteTrackPointsAndWaypoints(trackId); deleteTrackPointsAndWaypoints(trackId);
// Delete track folder resources. // Delete track folder resources.
@@ -200,7 +204,7 @@ public class ContentProviderUtils {
* *
* @param trackId the track id * @param trackId the track id
*/ */
private void deleteTrackPointsAndWaypoints(Track.Id trackId) { private void deleteTrackPointsAndWaypoints(@NonNull Track.Id trackId) {
String[] selectionArgs = new String[]{Long.toString(trackId.getId())}; String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs); contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs);
@@ -297,7 +301,7 @@ public class ContentProviderUtils {
TrackStatistics trackStatistics = track.getTrackStatistics(); TrackStatistics trackStatistics = track.getTrackStatistics();
// Value < 0 indicates no id is available // Value < 0 indicates no id is available
if (track.getId().isValid()) { if (track.getId() != null && track.getId().isValid()) {
values.put(TracksColumns._ID, track.getId().getId()); values.put(TracksColumns._ID, track.getId().getId());
} }
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid())); values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
@@ -513,7 +517,7 @@ public class ContentProviderUtils {
* @param waypoint the waypoint * @param waypoint the waypoint
* @return the content provider URI of the inserted waypoint. * @return the content provider URI of the inserted waypoint.
*/ */
public Uri insertWaypoint(Waypoint waypoint) { public Uri insertWaypoint(@NonNull Waypoint waypoint) {
waypoint.setId(null); waypoint.setId(null);
return contentResolver.insert(WaypointsColumns.CONTENT_URI, createContentValues(waypoint)); return contentResolver.insert(WaypointsColumns.CONTENT_URI, createContentValues(waypoint));
} }
@@ -556,14 +560,16 @@ public class ContentProviderUtils {
ContentValues createContentValues(@NonNull Waypoint waypoint) { ContentValues createContentValues(@NonNull Waypoint waypoint) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
if (waypoint.getId().isValid()) { if (waypoint.getId() != null && waypoint.getId().isValid()) {
values.put(WaypointsColumns._ID, waypoint.getId().getId()); values.put(WaypointsColumns._ID, waypoint.getId().getId());
} }
values.put(WaypointsColumns.NAME, waypoint.getName()); values.put(WaypointsColumns.NAME, waypoint.getName());
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription()); values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription());
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory()); values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
values.put(WaypointsColumns.ICON, waypoint.getIcon()); values.put(WaypointsColumns.ICON, waypoint.getIcon());
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId()); if (waypoint.getTrackId() != null) {
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId());
}
values.put(WaypointsColumns.LENGTH, waypoint.getLength()); values.put(WaypointsColumns.LENGTH, waypoint.getLength());
values.put(WaypointsColumns.DURATION, waypoint.getDuration()); values.put(WaypointsColumns.DURATION, waypoint.getDuration());
@@ -693,7 +699,8 @@ public class ContentProviderUtils {
*/ */
@Deprecated @Deprecated
public long getLastTrackPointId(Track.Id trackId) { public long getLastTrackPointId(Track.Id trackId) {
if (!trackId.isValid()) { if (trackId == null || !trackId.isValid()) {
Log.w(TAG, "Fix callers who do this.");
return -1L; return -1L;
} }
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=?)"; String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=?)";
@@ -86,7 +86,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
StringBuilder trackIdBuilder = new StringBuilder(); StringBuilder trackIdBuilder = new StringBuilder();
for (Track.Id trackId : trackIds) { for (Track.Id trackId : trackIds) {
trackIdBuilder.append(trackId).append(TRACKID_DELIMITER); trackIdBuilder.append(trackId.getId()).append(TRACKID_DELIMITER);
} }
trackIdBuilder.deleteCharAt(trackIdBuilder.lastIndexOf(TRACKID_DELIMITER)); trackIdBuilder.deleteCharAt(trackIdBuilder.lastIndexOf(TRACKID_DELIMITER));
@@ -221,7 +221,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> { PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) { try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
trackExporter.writeTrack(tracks, fileOutputStream); trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, "there occurred an error while sharing a file: " + e); Log.w(TAG, "there occurred an error while sharing a file: " + e);
} }
@@ -138,7 +138,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
notificationManager = new TrackRecordingServiceNotificationManager(this); notificationManager = new TrackRecordingServiceNotificationManager(this);
// onSharedPreferenceChanged might not set recordingTrackId. // onSharedPreferenceChanged might not set recordingTrackId.
recordingTrackId = new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT); recordingTrackId = null;
PreferencesUtils.register(this, sharedPreferenceChangeListener); PreferencesUtils.register(this, sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null); sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
@@ -333,7 +333,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
if (track == null) { if (track == null) {
if (isRecording()) { if (isRecording()) {
Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId); Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId);
updateRecordingState(new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT), true); updateRecordingState(null, true);
} }
showNotification(false); showNotification(false);
return; return;
@@ -406,7 +406,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
Track.Id trackId = recordingTrackId; Track.Id trackId = recordingTrackId;
boolean wasPaused = recordingTrackPaused; boolean wasPaused = recordingTrackPaused;
updateRecordingState(new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT), true); updateRecordingState(null, true);
// Update database // Update database
Track track = contentProviderUtils.getTrack(trackId); Track track = contentProviderUtils.getTrack(trackId);
@@ -509,7 +509,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
*/ */
private void updateRecordingState(Track.Id trackId, boolean paused) { private void updateRecordingState(Track.Id trackId, boolean paused) {
recordingTrackId = trackId; recordingTrackId = trackId;
PreferencesUtils.setLong(this, R.string.recording_track_id_key, trackId.getId()); long currentTrackId = trackId != null ? trackId.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
PreferencesUtils.setLong(this, R.string.recording_track_id_key, currentTrackId);
recordingTrackPaused = paused; recordingTrackPaused = paused;
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused); PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
} }
@@ -78,7 +78,7 @@ public interface TrackRecordingServiceInterface {
boolean isPaused(); boolean isPaused();
/** /**
* Gets the current recording track ID. Returns -1 if not recording. * Gets the current recording track ID. Returns null if not recording.
*/ */
Track.Id getRecordingTrackId(); Track.Id getRecordingTrackId();
@@ -354,7 +354,10 @@ public class PreferencesUtils {
@Deprecated @Deprecated
//TODO Method is very misleading: it only checks if the provided trackId not the default value (i.e., not recording). //TODO Method is very misleading: it only checks if the provided trackId not the default value (i.e., not recording).
public static boolean isRecording(Track.Id recordingTrackId) { public static boolean isRecording(Track.Id recordingTrackId) {
return recordingTrackId == null || recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT; if (recordingTrackId == null) {
return false;
}
return recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT;
} }
public static void resetPreferences(Context context, boolean readAgain) { public static void resetPreferences(Context context, boolean readAgain) {