Track recording service handles the status of recording: track's id, pause and GPS status.

Created an interface for all components that want to know about recording status through the service.
All interface's methods are optional so every component can implement what they need.
Fixes #388.
This commit is contained in:
Román Martínez
2021-03-23 20:08:25 +01:00
parent f3cf1a2571
commit e09b04308c
26 changed files with 426 additions and 466 deletions
@@ -606,7 +606,7 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertMarker(marker3);
contentProviderUtils.insertMarker(marker4);
assertEquals(4, contentProviderUtils.getNextMarkerNumber(trackId));
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
}
/**
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
import android.os.Looper;
import android.util.Log;
import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
import androidx.test.rule.GrantPermissionRule;
@@ -215,7 +216,9 @@ public class ExportImportTest {
@Test(expected = ImportAlreadyExistsException.class)
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
// given
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, true);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
editor.commit();
Track track = contentProviderUtils.getTrack(trackId);
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
@@ -285,7 +288,9 @@ public class ExportImportTest {
@Test(expected = ImportAlreadyExistsException.class)
public void gpx_duplicate_trackUUID() {
// given
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, true);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
editor.commit();
Track track = contentProviderUtils.getTrack(trackId);
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
@@ -38,6 +38,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalUnit;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@@ -151,31 +154,30 @@ public class TrackRecordingServiceTest {
// then
// Test if we start in no-recording mode by default.
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@Test
public void testRecording_oldTracks() throws TimeoutException {
// given
createDummyTrack(trackId, false);
createDummyTrack(trackId);
// when
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// then
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@Test
public void testRecording_serviceRestart_whileRecording() throws TimeoutException {
// given
createDummyTrack(trackId, true);
createDummyTrack(trackId);
//when
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.resumeTrack(trackId);
// then
assertTrue(service.isRecording());
@@ -233,7 +235,6 @@ public class TrackRecordingServiceTest {
// then
assertTrue(service.isRecording());
assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
assertEquals(3, trackPoints.size());
@@ -259,7 +260,6 @@ public class TrackRecordingServiceTest {
// then
assertTrue(service.isRecording());
assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
assertEquals(4, trackPoints.size());
@@ -285,7 +285,6 @@ public class TrackRecordingServiceTest {
// then
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
assertEquals(2, trackPoints.size());
@@ -293,41 +292,20 @@ public class TrackRecordingServiceTest {
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
}
@Ignore("Sometimes fails on CI.")
@MediumTest
@Test
public void testRecording_orphanedRecordingTrack() throws TimeoutException {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// when
// Just set recording track to a bogus value.
// Make sure that the service will not start recording and will clear the bogus track.
PreferencesUtils.setLong(sharedPreferences, context, R.string.recording_track_id_key, 123L);
// then
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
}
@MediumTest
@Test
public void testStartNewTrack_alreadyRecording() throws TimeoutException {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Track.Id trackId = service.startNewTrack();
assertTrue(service.isRecording());
Track.Id trackId = service.getRecordingTrackId();
// when
Track.Id newTrackId = service.startNewTrack();
// then
assertNotNull(trackId);
assertNull(newTrackId);
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
assertEquals(trackId, service.getRecordingTrackId());
}
@MediumTest
@@ -342,8 +320,7 @@ public class TrackRecordingServiceTest {
service.endCurrentTrack();
// then
assertFalse(PreferencesUtils.isRecording(sharedPreferences, context));
assertNull(service.getRecordingTrackId());
assertFalse(service.isRecording());
}
@MediumTest
@@ -365,10 +342,9 @@ public class TrackRecordingServiceTest {
public void testInsertWaypointMarker_validWaypoint() throws TimeoutException, InterruptedException {
// given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Track.Id trackId = service.startNewTrack();
assertTrue(service.isRecording());
newTrackPoint(service);
Track.Id trackId = service.getRecordingTrackId();
// when
Marker.Id markerId = service.insertMarker(null, null, null, null);
@@ -385,22 +361,23 @@ public class TrackRecordingServiceTest {
service.endCurrentTrack();
}
private void addTrack(Track track, boolean isRecording) {
private void addTrack(Track track) {
assertNotNull(track.getId());
contentProviderUtils.insertTrack(track);
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
PreferencesUtils.setLong(sharedPreferences, context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.recording_track_paused_key, !isRecording);
}
// NOTE: Do not use to create a track that is currently recording.
private void createDummyTrack(Track.Id id, boolean isRecording) {
private void createDummyTrack(Track.Id id) {
Track dummyTrack = new Track();
dummyTrack.setId(id);
dummyTrack.setName("Dummy Track");
TrackStatistics trackStatistics = new TrackStatistics();
Instant now = Instant.now();
trackStatistics.setStartTime(now.minusSeconds(5L));
trackStatistics.setStopTime(now.minusSeconds(1L));
dummyTrack.setTrackStatistics(trackStatistics);
addTrack(dummyTrack, isRecording);
addTrack(dummyTrack);
}
private static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
import android.location.Location;
import android.os.Looper;
import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
@@ -35,7 +36,6 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -99,28 +99,36 @@ public class TrackRecordingServiceTestLooper {
@MediumTest
@Test
public void testWithProperties_minRequiredAccuracy() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, 500);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), 500);
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.voice_frequency_key), Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_voiceFrequencyByDistance() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, -1);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.voice_frequency_key), -1);
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_voiceFrequencyByTime() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, 1);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.voice_frequency_key), 1);
editor.commit();
fullRecordingSession();
}
@@ -128,29 +136,36 @@ public class TrackRecordingServiceTestLooper {
@MediumTest
@Test
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.max_recording_distance_key, Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.max_recording_distance_key), Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_maxRecordingDistance() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.max_recording_distance_key, 50);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.max_recording_distance_key), 50);
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
int minRecordingIntervalDefault = Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_distance_interval_key, minRecordingIntervalDefault);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.recording_distance_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_minRecordingDistance() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_distance_interval_key, 2);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.recording_distance_interval_key), 2);
editor.commit();
fullRecordingSession();
}
@@ -171,22 +186,27 @@ public class TrackRecordingServiceTestLooper {
@MediumTest
@Test
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.min_recording_interval_key, Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.min_recording_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_minRecordingInterval() throws TimeoutException {
PreferencesUtils.setInt(sharedPreferences, context, R.string.min_recording_interval_key, 2);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.min_recording_interval_key), 2);
editor.commit();
fullRecordingSession();
}
@MediumTest
@Test
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
int recordingGPSAccuracyDefault = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, recordingGPSAccuracyDefault);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default)));
editor.commit();
fullRecordingSession();
}
@@ -209,8 +229,6 @@ public class TrackRecordingServiceTestLooper {
Track track = contentProviderUtils.getTrack(trackId);
assertNotNull(track);
assertEquals(trackId, track.getId());
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
assertEquals(trackId, service.getRecordingTrackId());
// Insert a few points, markers and statistics.
long startTime = System.currentTimeMillis();
@@ -234,7 +252,6 @@ public class TrackRecordingServiceTestLooper {
// Stop the track. Validate if it has correct data.
service.endCurrentTrack();
assertFalse(service.isRecording());
assertNull(service.getRecordingTrackId());
track = contentProviderUtils.getTrack(trackId);
assertNotNull(track);
assertEquals(trackId, track.getId());
@@ -26,7 +26,6 @@ import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An abstract class for the following common tasks across
@@ -44,6 +43,8 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
protected static final int GPS_REQUEST_CODE = 6;
private static final int DELETE_REQUEST_CODE = 3;
protected Track.Id recordingTrackId;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DELETE_REQUEST_CODE) {
@@ -66,7 +67,6 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
public void onConfirmDeleteDone(Track.Id... trackIds) {
boolean stopRecording = false;
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(PreferencesUtils.getSharedPreferences(this), this);
for (Track.Id trackId : trackIds) {
if (trackId.equals(recordingTrackId)) {
stopRecording = true;
@@ -75,7 +75,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
}
if (stopRecording) {
getTrackRecordingServiceConnection().stopRecording(this, false);
getTrackRecordingServiceConnection().stopRecording(this);
}
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
@@ -18,8 +18,6 @@ package de.dennisguse.opentracks;
import android.app.SearchManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -42,16 +40,18 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.databinding.MarkerListBinding;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* Activity to show a list of markers in a track.
*
* @author Leif Hendrik Wilden
*/
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller {
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller, TrackRecordingServiceStatus.Listener {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -59,25 +59,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, MarkerListActivity.this);
}
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, MarkerListActivity.this);
}
if (key != null) {
runOnUiThread(MarkerListActivity.this::invalidateOptionsMenu);
}
}
};
private Track track;
private MarkerResourceCursorAdapter resourceCursorAdapter;
@@ -85,6 +68,18 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
private final MarkerLoaderCallback loaderCallbacks = new MarkerLoaderCallback();
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private final Runnable bindCallback = new Runnable() {
@Override
public void run() {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(MarkerListActivity.this);
}
}
};
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@Override
@@ -112,10 +107,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
super.onCreate(savedInstanceState);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
Track.Id trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
@@ -132,18 +125,20 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
viewBinding.markerList.setOnScrollListener(scrollVisibleViews);
viewBinding.markerList.setAdapter(resourceCursorAdapter);
ActivityUtils.configureListViewContextualMenu(viewBinding.markerList, contextualActionModeCallback);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
}
@Override
protected void onStart() {
super.onStart();
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
trackRecordingServiceConnection.startConnection(this);
}
@Override
protected void onResume() {
super.onResume();
trackRecordingServiceConnection.bind(this);
this.invalidateOptionsMenu();
loadData(getIntent());
}
@@ -151,7 +146,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
@Override
protected void onStop() {
super.onStop();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
trackRecordingServiceConnection.unbind(this);
}
@Override
@@ -161,7 +156,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
viewBinding = null;
resourceCursorAdapter = null;
contentProviderUtils = null;
sharedPreferences = null;
}
@Override
@@ -350,4 +344,14 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
resourceCursorAdapter.swapCursor(null);
}
}
@Override
public void onTrackRecordingPaused(boolean isPaused) {
recordingTrackPaused = isPaused;
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
}
}
@@ -24,10 +24,10 @@ public class Startup extends Application {
public void onCreate() {
super.onCreate();
//Include version information into stack traces.
// Include version information into stack traces.
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
//Set default values of preferences on first start.
// Set default values of preferences on first start.
SharedPreferences sharedPreferences = PreferencesUtils.resetPreferences(this, false);
if (PreferencesUtils.getString(sharedPreferences, this, R.string.stats_units_key, "").equals("")) {
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
@@ -36,13 +36,7 @@ public class Startup extends Application {
ActivityUtils.applyNightMode(sharedPreferences, this);
//TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed.
if (PreferencesUtils.isRecording(sharedPreferences, this)) {
Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed.");
PreferencesUtils.setLong(sharedPreferences, this, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
}
//In debug builds: show thread and VM warnings.
// In debug builds: show thread and VM warnings.
if (BuildConfig.DEBUG) {
Log.d(TAG, "Enabling strict mode");
StrictMode.enableDefaults();
@@ -129,7 +129,7 @@ public class TrackController implements View.OnTouchListener {
buttonDelay = () -> {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
view.performClick();
callback.recordStart(); //TODO Should be recordPause();
callback.recordPause();
transition.resetTransition();
@@ -239,7 +239,7 @@ public class TrackController implements View.OnTouchListener {
public interface Callback {
void recordStart();
void recordPause();
void recordStop();
}
}
@@ -52,6 +52,7 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.databinding.TrackListBinding;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
@@ -69,7 +70,7 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
*
* @author Leif Hendrik Wilden
*/
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback {
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback, TrackRecordingServiceStatus.Listener {
private static final String TAG = TrackListActivity.class.getSimpleName();
@@ -86,7 +87,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Preferences
private boolean metricUnits = true;
private Track.Id recordingTrackId = null;
private boolean recordingTrackPaused = true;
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@@ -105,29 +107,17 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
}
};
private boolean recordingTrackPaused;
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, TrackListActivity.this);
}
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, TrackListActivity.this);
if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
trackRecordingServiceConnection.startAndBind(TrackListActivity.this);
}
}
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, TrackListActivity.this);
}
if (key != null) {
runOnUiThread(() -> {
TrackListActivity.this.invalidateOptionsMenu();
loaderCallbacks.restart();
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
trackController.update(isRecording, recordingTrackPaused);
trackController.update(isRecording(), recordingTrackPaused);
});
}
}
@@ -141,10 +131,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private final Runnable bindChangedCallback = new Runnable() {
@Override
public void run() {
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
// After binding changes (e.g., becomes available), update the total time in trackController.
runOnUiThread(() -> trackController.update(isRecording, recordingTrackPaused));
runOnUiThread(() -> trackController.update(isRecording(), recordingTrackPaused));
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
@@ -155,11 +143,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Get GPS status and listen GPS status changes.
gpsStatusValue = service.getGpsStatus();
updateGpsMenuItem(true, isRecording);
service.addListener(newStatus -> {
gpsStatusValue = newStatus;
updateGpsMenuItem(true, isRecording);
});
updateGpsMenuItem(true, isRecording());
service.addListener(TrackListActivity.this);
if (isGpsStarted()) {
return;
@@ -167,7 +152,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
service.startGps();
gpsStatusValue = GpsStatusValue.GPS_ENABLED;
updateGpsMenuItem(true, isRecording);
updateGpsMenuItem(true, isRecording());
}
};
@@ -181,8 +166,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
gpsStatusValue = GpsStatusValue.GPS_NONE;
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
@@ -197,7 +180,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackId) -> {
if (trackId == recordingTrackId.getId()) {
if (isRecording() && trackId == recordingTrackId.getId()) {
// Is recording -> open record activity.
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
@@ -281,7 +264,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Update UI
this.invalidateOptionsMenu();
LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);
trackController.onResume(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
trackController.onResume(isRecording(), recordingTrackPaused);
}
@Override
@@ -322,9 +305,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isGpsStarted = isGpsStarted();
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
updateMenuItems(isGpsStarted, isRecording);
updateMenuItems(isGpsStarted(), isRecording());
SearchView searchView = (SearchView) searchMenuItem.getActionView();
searchView.setQuery("", false);
@@ -572,7 +553,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
@Override
public void recordStart() {
if (!PreferencesUtils.isRecording(recordingTrackId)) {
if (recordingTrackId == null) {
// Not recording -> Recording
updateMenuItems(false, true);
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
@@ -582,17 +563,45 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
updateMenuItems(false, true);
trackRecordingServiceConnection.resumeTrack();
trackController.update(true, false);
} else {
// Recording -> Paused
updateMenuItems(false, true);
trackRecordingServiceConnection.pauseTrack();
trackController.update(true, true);
}
}
@Override
public void recordPause() {
updateMenuItems(false, true);
trackRecordingServiceConnection.pauseTrack();
trackController.update(true, true);
}
@Override
public void recordStop() {
updateMenuItems(false, false);
trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
}
private boolean isRecording() {
return recordingTrackId != null;
}
@Override
public void onGpsStatus(GpsStatusValue newStatus) {
gpsStatusValue = newStatus;
updateGpsMenuItem(true, isRecording());
}
@Override
public void onTrackRecordingPaused(boolean isPaused) {
if (recordingTrackPaused != isPaused) {
trackController.update(isRecording(), isPaused);
}
recordingTrackPaused = isPaused;
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
if (!isRecording() && trackId != null) {
trackController.update(true, recordingTrackPaused);
}
recordingTrackId = trackId;
}
}
@@ -40,11 +40,12 @@ import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.IntervalsFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An activity to show the track detail, record a new track or resumes an existing one.
@@ -52,7 +53,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public class TrackRecordedActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface {
public class TrackRecordedActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface, TrackRecordingServiceStatus.Listener {
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
@@ -71,6 +72,18 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
private Track.Id trackId;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private final Runnable bindCallback = new Runnable() {
@Override
public void run() {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(TrackRecordedActivity.this);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -88,6 +101,8 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
}
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
postponeEnterTransition();
}
@@ -107,11 +122,14 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
if (trackId != null) {
trackDataHub.loadTrack(trackId);
}
trackRecordingServiceConnection.bind(this);
}
@Override
protected void onStop() {
super.onStop();
trackRecordingServiceConnection.unbind(this);
trackDataHub.stop();
}
@@ -143,7 +161,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.track_detail_markers).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.findItem(R.id.track_detail_resume_track).setVisible(!PreferencesUtils.isRecording(PreferencesUtils.getSharedPreferences(this), this));
menu.findItem(R.id.track_detail_resume_track).setVisible(recordingTrackId == null);
Track track = contentProviderUtils.getTrack(trackId);
setTitle(track != null ? track.getName() : "");
return super.onPrepareOptionsMenu(menu);
@@ -285,4 +303,9 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
ViewCompat.setTransitionName(viewIcon, TrackRecordedActivity.VIEW_TRACK_ICON);
startPostponedEnterTransition();
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
}
}
@@ -26,6 +26,7 @@ import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.IntervalsFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.SettingsActivity;
@@ -43,7 +44,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
*/
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
//This mode of communication should be removed anyhow.
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback {
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback, TrackRecordingServiceStatus.Listener {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -62,8 +63,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
private Track.Id trackId;
// Preferences
private boolean recordingTrackPaused;
private final Runnable bindChangedCallback = new Runnable() {
@@ -78,7 +77,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
return;
}
if (!service.isRecording()) {
// Starts or resumes a track.
if (trackId == null) {
// trackId isn't initialized -> leads a new recording.
trackId = service.startNewTrack();
@@ -92,31 +90,23 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackController.update(true, false);
trackController.onResume(true, recordingTrackPaused);
}
service.addListener(TrackRecordingActivity.this);
}
};
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, TrackRecordingActivity.this);
setLockscreenPolicy();
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
setLockscreenPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_fullscreen_while_recording_key, key)) {
setFullscreenPolicy();
}
if (key == null) return;
runOnUiThread(() -> {
TrackRecordingActivity.this.invalidateOptionsMenu();
trackController.update(true, recordingTrackPaused);
@@ -145,8 +135,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
}
}
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
trackDataHub = new TrackDataHub(this);
@@ -345,22 +333,21 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
@Override
public void recordStart() {
if (recordingTrackPaused) {
// Paused -> Resume
updateMenuItems(false);
trackRecordingServiceConnection.resumeTrack();
trackController.update(true, false);
} else {
// Recording -> Paused
updateMenuItems(true);
trackRecordingServiceConnection.pauseTrack();
trackController.update(true, true);
}
updateMenuItems(false);
trackRecordingServiceConnection.resumeTrack();
trackController.update(true, false);
}
@Override
public void recordPause() {
updateMenuItems(true);
trackRecordingServiceConnection.pauseTrack();
trackController.update(true, true);
}
@Override
public void recordStop() {
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
startActivity(newIntent);
@@ -412,4 +399,22 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
}
}
}
@Override
public void onTrackRecordingPaused(boolean isPaused) {
if (recordingTrackPaused != isPaused) {
trackController.update(true, isPaused);
}
recordingTrackPaused = isPaused;
setLockscreenPolicy();
setScreenOnPolicy();
}
@Override
public void onTrackRecordingId(Track.Id newTrackId) {
if (newTrackId != null && !newTrackId.equals(trackId)) {
trackId = newTrackId;
trackController.update(true, recordingTrackPaused);
}
}
}
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.content;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Handler;
@@ -31,7 +30,6 @@ import androidx.annotation.VisibleForTesting;
import java.util.Collections;
import java.util.Set;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track;
@@ -40,7 +38,9 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
/**
* Track data hub.
@@ -50,7 +50,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
*
* @author Rodrigo Damazio
*/
public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeListener {
public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
/**
* Target number of track points displayed by the diagrams (recommended).
@@ -71,12 +71,12 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
private final ContentProviderUtils contentProviderUtils;
private final int targetNumPoints;
private SharedPreferences sharedPreferences;
private boolean started;
private HandlerThread handlerThread;
private Handler handler;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
// Preference values
private Track.Id selectedTrackId;
private Track.Id recordingTrackId;
@@ -92,6 +92,13 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
private ContentObserver markersTableObserver;
private ContentObserver trackPointsTableObserver;
private final Runnable bindCallback = () -> {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(TrackDataHub.this);
}
};
public TrackDataHub(Context context) {
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACKPOINTS);
}
@@ -141,10 +148,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
};
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, false, trackPointsTableObserver);
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(sharedPreferences, null);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
trackRecordingServiceConnection.bind(context);
handler.post(() -> {
if (started) {
@@ -159,9 +164,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
return;
}
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
sharedPreferences = null;
started = false;
//Unregister listeners
@@ -175,6 +177,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
handlerThread = null;
}
handler = null;
trackRecordingServiceConnection.unbind(context);
}
public void loadTrack(final @NonNull Track.Id trackId) {
@@ -215,7 +219,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* Returns true if the selected track is recording.
*/
public boolean isSelectedTrackRecording() {
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && PreferencesUtils.isRecording(recordingTrackId);
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId);
}
/**
@@ -225,18 +229,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
handler.post(() -> {
if (PreferencesUtils.isKey(context, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, context);
}
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, context);
}
});
}
/**
* Loads data for all listeners. To be run in the {@link #handler} thread.
*/
@@ -432,4 +424,14 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
firstSeenTrackPointId = null;
lastSeenTrackPointId = null;
}
@Override
public void onTrackRecordingPaused(boolean isPaused) {
recordingTrackPaused = isPaused;
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
}
}
@@ -362,9 +362,9 @@ public class ContentProviderUtils {
}
/**
* @return -1 if not able to get the next marker number.
* @return null if not able to get the next marker number.
*/
public int getNextMarkerNumber(@NonNull Track.Id trackId) {
public Integer getNextMarkerNumber(@NonNull Track.Id trackId) {
String[] projection = {MarkerColumns._ID};
String selection = MarkerColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
@@ -373,7 +373,7 @@ public class ContentProviderUtils {
return cursor.getCount();
}
}
return -1;
return null;
}
public Marker getMarker(@NonNull Marker.Id markerId) {
@@ -40,7 +40,6 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* Imports a KMZ file.
@@ -79,7 +78,6 @@ public class KmzTrackImporter implements TrackImporter {
trackIdsWithImages.add(trackId);
deleteOrphanImages(context, trackId);
} else {
cleanImport(context, trackId);
return new ArrayList<>();
}
}
@@ -230,18 +228,6 @@ public class KmzTrackImporter implements TrackImporter {
}
}
/**
* Cleans up import.
*
* @param trackId the trackId
*/
private void cleanImport(Context context, Track.Id trackId) {
if (PreferencesUtils.isRecording(trackId)) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
contentProviderUtils.deleteTrack(context, trackId);
}
}
private List<Track.Id> parseKml(ZipInputStream zipInputStream) {
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context);
@@ -37,8 +37,6 @@ import androidx.core.app.TaskStackBuilder;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackListActivity;
@@ -48,7 +46,6 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.io.file.exporter.ExportServiceResultReceiver;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
@@ -83,23 +80,13 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
private TrackRecordingServiceNotificationManager notificationManager;
private SharedPreferences sharedPreferences;
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
private int recordingDistanceInterval;
private int maxRecordingDistance;
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Context context = TrackRecordingService.this;
if (PreferencesUtils.isKey(TrackRecordingService.this, R.string.recording_track_id_key, key)) {
// Only through the TrackRecordingService can one stop a recording and set the recordingTrackId to -1L.
if (PreferencesUtils.isRecording(sharedPreferences, TrackRecordingService.this)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, TrackRecordingService.this);
}
}
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, context);
}
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
voiceExecutor.setMetricUnits(metricUnits);
@@ -132,7 +119,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
private HandlerServer handlerServer;
private List<TrackRecordingServiceCallback> listeners = new ArrayList<>();
private final TrackRecordingServiceStatus serviceStatus = new TrackRecordingServiceStatus();
@Override
public void onCreate() {
@@ -145,14 +132,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
notificationManager = new TrackRecordingServiceNotificationManager(this);
// onSharedPreferenceChanged might not set recordingTrackId.
recordingTrackId = null;
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
restartTrackAfterServiceRestart();
}
@Override
@@ -167,13 +149,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
@Override
public void onDestroy() {
if (listeners != null) {
for (TrackRecordingServiceCallback listener : listeners) {
listener.onGpsStatusChange(GpsStatusValue.GPS_NONE);
}
listeners.clear();
listeners = null;
}
serviceStatus.onStop();
handlerServer.stop(this);
handlerServer = null;
@@ -212,15 +188,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
}
public boolean isRecording() {
return PreferencesUtils.isRecording(recordingTrackId);
return serviceStatus.isRecording();
}
public boolean isPaused() {
return recordingTrackPaused;
}
public Track.Id getRecordingTrackId() {
return recordingTrackId;
return serviceStatus.getRecordingTrackPaused();
}
public TrackStatistics getTrackStatistics() {
@@ -250,14 +222,14 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
}
if (name == null) {
int nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(recordingTrackId);
if (nextMarkerNumber == -1) {
Integer nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(serviceStatus.getRecordingTrackId());
if (nextMarkerNumber == null) {
nextMarkerNumber = 1;
}
name = getString(R.string.marker_name_format, nextMarkerNumber + 1);
}
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(recordingTrackId);
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(serviceStatus.getRecordingTrackId());
if (trackPoint == null) {
Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
return null;
@@ -273,7 +245,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
long duration = stats.getTotalTime().toMillis();
// Insert marker
Marker marker = new Marker(name, description, category, icon, recordingTrackId, length, duration, trackPoint, photoUrl);
Marker marker = new Marker(name, description, category, icon, serviceStatus.getRecordingTrackId(), length, duration, trackPoint, photoUrl);
Uri uri = contentProviderUtils.insertMarker(marker);
return new Marker.Id(ContentUris.parseId(uri));
}
@@ -294,8 +266,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
Uri uri = contentProviderUtils.insertTrack(track);
Track.Id trackId = new Track.Id(ContentUris.parseId(uri));
// Update shared preferences
updateRecordingState(trackId, false);
// Set recording status
serviceStatus.onChange(trackId, false);
// Update database
track.setId(trackId);
@@ -336,48 +308,23 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
// Update shared preferences.
updateRecordingState(trackId, false);
// Set recording status
serviceStatus.onChange(trackId, false);
startRecording();
}
/**
* Try to restart the previous recording track in case the service has been restarted by the system, which can sometimes happen.
*/
private void restartTrackAfterServiceRestart() {
Track track = contentProviderUtils.getTrack(recordingTrackId);
if (track == null) {
if (isRecording()) {
Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId.getId());
updateRecordingState(null, true);
}
showNotification(false);
return;
}
Log.d(TAG, "Restarting track: " + track.getId());
trackStatisticsUpdater = new TrackStatisticsUpdater();
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
trackStatisticsUpdater.addTrackPoint(trackPointIterator, recordingDistanceInterval);
} catch (RuntimeException e) {
Log.e(TAG, "RuntimeException", e);
}
startRecording();
}
void resumeCurrentTrack() {
if (!isRecording() || !isPaused()) {
Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
return;
}
updateRecordingState(recordingTrackId, false);
// Set recording status
serviceStatus.onChange(false);
// Update database
Track track = contentProviderUtils.getTrack(recordingTrackId);
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
if (track != null) {
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
}
@@ -417,17 +364,18 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
showNotification(true);
}
void endCurrentTrack() {
Track.Id endCurrentTrack() {
if (!isRecording()) {
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
return;
return null;
}
// Need to remember the recordingTrackId before setting it to -1L
// Need to remember the recordingTrackId before setting it to null
boolean wasPause = isPaused();
Track.Id trackId = recordingTrackId;
Track.Id trackId = serviceStatus.getRecordingTrackId();
updateRecordingState(null, true);
// Set recording status
serviceStatus.onChange(null, true);
if (!wasPause) {
// Update database
@@ -445,6 +393,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
ExportUtils.postWorkoutExport(this, track, new ExportServiceResultReceiver(new Handler(), this));
endRecording(true);
return trackId;
}
void pauseCurrentTrack() {
@@ -453,10 +403,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return;
}
updateRecordingState(recordingTrackId, true);
// Set recording status
serviceStatus.onChange(true);
// Update database
Track track = contentProviderUtils.getTrack(recordingTrackId);
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
if (track != null) {
if (lastTrackPoint != null) {
insertTrackPointIfNewer(track, lastTrackPoint);
@@ -528,21 +479,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return lastTrackPoint != null;
}
/**
* Updates the recording states.
* This will inform subscribed {@link OnSharedPreferenceChangeListener}.
*
* @param trackId the recording track id
* @param paused true if the recording is paused
*/
private void updateRecordingState(Track.Id trackId, boolean paused) {
recordingTrackId = trackId;
long currentTrackId = trackId != null ? trackId.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
PreferencesUtils.setLong(sharedPreferences, this, R.string.recording_track_id_key, currentTrackId);
recordingTrackPaused = paused;
PreferencesUtils.setBoolean(sharedPreferences, this, R.string.recording_track_paused_key, recordingTrackPaused);
}
@Override
public void newTrackPoint(TrackPoint trackPoint, int recordingGpsAccuracy) {
if (!isRecording() || isPaused()) {
@@ -550,7 +486,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return;
}
Track track = contentProviderUtils.getTrack(recordingTrackId);
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
if (track == null) {
Log.w(TAG, "Ignore newTrackPoint. No track.");
return;
@@ -623,16 +559,12 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
@Override
public void newGpsStatus(GpsStatusValue gpsStatusValue) {
if (listeners != null) {
notificationManager.updateContent(getString(gpsStatusValue.message));
for (TrackRecordingServiceCallback listener : listeners) {
listener.onGpsStatusChange(gpsStatusValue);
}
}
notificationManager.updateContent(getString(gpsStatusValue.message));
serviceStatus.onChange(gpsStatusValue);
}
public void addListener(TrackRecordingServiceCallback listener) {
listeners.add(listener);
public void addListener(@NonNull TrackRecordingServiceStatus.Listener listener) {
serviceStatus.addListener(listener);
}
/**
@@ -724,7 +656,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
private void showNotification(boolean isGpsStarted) {
if (isRecording()) {
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, recordingTrackId);
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, serviceStatus.getRecordingTrackId());
PendingIntent pendingIntent = TaskStackBuilder.create(this)
.addParentStack(TrackRecordingActivity.class)
.addNextIntent(intent)
@@ -1,5 +1,6 @@
package de.dennisguse.opentracks.services;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
@@ -28,7 +29,7 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
}
@Override
public void addListener(TrackRecordingServiceCallback listener) {
public void addListener(@NonNull TrackRecordingServiceStatus.Listener listener) {
trackRecordingService.addListener(listener);
}
@@ -67,8 +68,8 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
}
@Override
public void endCurrentTrack() {
trackRecordingService.endCurrentTrack();
public Track.Id endCurrentTrack() {
return trackRecordingService.endCurrentTrack();
}
@Override
@@ -81,11 +82,6 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
return trackRecordingService.isPaused();
}
@Override
public Track.Id getRecordingTrackId() {
return trackRecordingService.getRecordingTrackId();
}
@Override
public Duration getTotalTime() {
return trackRecordingService.getTotalTime();
@@ -1,10 +0,0 @@
package de.dennisguse.opentracks.services;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
/**
* Interface all activities have to implements to receive information from the service.
*/
public interface TrackRecordingServiceCallback {
void onGpsStatusChange(GpsStatusValue newStatus);
}
@@ -20,7 +20,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
@@ -31,11 +30,7 @@ import androidx.annotation.NonNull;
import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackEditActivity;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* Wrapper for the track recording service.
@@ -64,6 +59,13 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
this.callback = callback;
}
public void bind(@NonNull Context context) {
if (trackRecordingService != null) {
return;
}
context.bindService(new Intent(context, TrackRecordingService.class), this, 0);
}
/**
* Starts and binds the service.
*/
@@ -202,30 +204,12 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
return null;
}
public void stopRecording(@NonNull Context context, boolean showEditor) {
public void stopRecording(@NonNull Context context) {
TrackRecordingServiceInterface trackRecordingService = getServiceIfBound();
if (trackRecordingService == null) {
Log.e(TAG, "TrackRecordingService not connected.");
} else {
try {
if (showEditor) {
// Need to remember the recordingTrackId before calling endCurrentTrack() as endCurrentTrack() sets the value to -1L.
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, context);
trackRecordingService.endCurrentTrack();
if (PreferencesUtils.isRecording(sharedPreferences, context)) {
Intent intent = IntentUtils.newIntent(context, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, recordingTrackId)
.putExtra(TrackEditActivity.EXTRA_NEW_TRACK, true);
context.startActivity(intent);
}
} else {
trackRecordingService.endCurrentTrack();
}
} catch (Exception e) {
//TODO What exception are we catching here? Should be removed...
Log.e(TAG, "Unable to stop recording.", e);
}
trackRecordingService.endCurrentTrack();
}
unbindAndStop(context);
}
@@ -15,6 +15,7 @@
*/
package de.dennisguse.opentracks.services;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
@@ -44,7 +45,12 @@ public interface TrackRecordingServiceInterface {
void resumeTrack(Track.Id trackId);
void endCurrentTrack();
/**
* End current track.
*
* @return the Track.Id of the track that is ended.
*/
Track.Id endCurrentTrack();
boolean isRecording();
@@ -53,11 +59,6 @@ public interface TrackRecordingServiceInterface {
*/
boolean isPaused();
/**
* Gets the current recording track ID. Returns null if not recording.
*/
Track.Id getRecordingTrackId();
/**
* Gets the total time for the current recording track. Returns 0 if not recording.
*/
@@ -100,7 +101,7 @@ public interface TrackRecordingServiceInterface {
@VisibleForTesting
void newTrackPoint(TrackPoint trackPoint, int recordingGpsAccuracy);
void addListener(TrackRecordingServiceCallback listener);
void addListener(@NonNull TrackRecordingServiceStatus.Listener listener);
GpsStatusValue getGpsStatus();
}
@@ -0,0 +1,79 @@
package de.dennisguse.opentracks.services;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
/**
* This class handles the status of the recording service.
* Also offer an interface through which to know the recording Track.Id, pause status and GPS status.
*/
public class TrackRecordingServiceStatus {
private Track.Id recordingTrackId;
private boolean recordingTrackPaused;
private final List<Listener> listeners = new ArrayList<>();
public void addListener(@NonNull Listener listener) {
if (this.listeners.contains(listener)) {
return;
}
this.listeners.add(listener);
listener.onTrackRecordingId(recordingTrackId);
listener.onTrackRecordingPaused(recordingTrackPaused);
}
public boolean getRecordingTrackPaused() {
return this.recordingTrackPaused;
}
public Track.Id getRecordingTrackId() {
return this.recordingTrackId;
}
public void onStop() {
for (Listener listener : listeners) {
listener.onGpsStatus(GpsStatusValue.GPS_NONE);
listener.onTrackRecordingId(null);
listener.onTrackRecordingPaused(true);
}
listeners.clear();
}
public boolean isRecording() {
return recordingTrackId != null;
}
void onChange(Track.Id trackId, boolean paused) {
recordingTrackId = trackId;
recordingTrackPaused = paused;
for (Listener listener : listeners) {
listener.onTrackRecordingId(recordingTrackId);
listener.onTrackRecordingPaused(recordingTrackPaused);
}
}
void onChange(boolean paused) {
recordingTrackPaused = paused;
for (Listener listener : listeners) {
listener.onTrackRecordingPaused(recordingTrackPaused);
}
}
void onChange(GpsStatusValue statusValue) {
for (Listener listener : listeners) {
listener.onGpsStatus(statusValue);
}
}
public interface Listener {
default void onGpsStatus(GpsStatusValue newValue) {}
default void onTrackRecordingPaused(boolean paused) {}
default void onTrackRecordingId(Track.Id trackId) {}
}
}
@@ -31,6 +31,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.AnnouncementUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -57,6 +58,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
private final ContentProviderUtils contentProviderUtils;
private Track.Id recordingTrackId;
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
@@ -139,6 +142,12 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
Log.e(TAG, "TrackRecordingService is null.");
return;
}
trackRecordingService.addListener(new TrackRecordingServiceStatus.Listener() {
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
}
});
announce(trackRecordingService.getTrackStatistics());
}
@@ -171,7 +180,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
return;
}
Track track = contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
Track track = contentProviderUtils.getTrack(recordingTrackId);
String category = track != null ? track.getCategory() : "";
//TODO Querying all TrackPoints all the time is inefficient; use TrackDataHub or something else.
@@ -20,8 +20,12 @@ import java.util.Locale;
import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingPowerPreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference;
@@ -89,14 +93,11 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, prefsFragment).commit();
}
public static class PrefsFragment extends PreferenceFragmentCompat {
public static class PrefsFragment extends PreferenceFragmentCompat implements TrackRecordingServiceStatus.Listener {
private SharedPreferences sharedPreferences;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
if (PreferencesUtils.isKey(getActivity(), R.string.recording_track_id_key, key)) {
getActivity().runOnUiThread(this::updateReset);
}
if (PreferencesUtils.isKey(getActivity(), R.string.stats_units_key, key)) {
getActivity().runOnUiThread(this::updateUnits);
}
@@ -105,6 +106,19 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
}
};
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private boolean isRecording;
private final Runnable bindServiceCallback = new Runnable() {
@Override
public void run() {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(PrefsFragment.this);
}
}
};
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
@@ -112,6 +126,8 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindServiceCallback);
try {
setPreferencesFromResource(R.xml.settings, rootKey);
} catch (ClassCastException e) {
@@ -137,13 +153,14 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
public void onResume() {
super.onResume();
trackRecordingServiceConnection.bind(getContext());
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
updateUnits();
updateReset();
updatePrefsDependOnRecording();
updateBluetooth();
updatePostWorkoutExport();
}
@@ -186,6 +203,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
@Override
public void onDestroy() {
super.onDestroy();
trackRecordingServiceConnection.unbind(getContext());
sharedPreferences = null;
}
@@ -195,11 +213,18 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
}
}
private void updateReset() {
final boolean isRecording = PreferencesUtils.isRecording(sharedPreferences, getActivity());
private void updatePrefsDependOnRecording() {
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
Preference importPreference = findPreference(getString(R.string.settings_import));
Preference exportPreference = findPreference(getString(R.string.settings_export));
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
importPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
exportPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
resetPreference.setEnabled(!isRecording);
importPreference.setEnabled(!isRecording);
exportPreference.setEnabled(!isRecording);
}
private void updateBluetooth() {
@@ -266,6 +291,14 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
return false;
});
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
if (trackId != null && !isRecording && isAdded()) {
isRecording = true;
updatePrefsDependOnRecording();
}
}
}
}
@@ -27,7 +27,6 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.preference.PreferenceManager;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
/**
@@ -47,21 +46,6 @@ public class PreferencesUtils {
return PreferenceManager.getDefaultSharedPreferences(context);
}
@Deprecated
//NOTE: is at the moment still used to determine if a track is currently recorded; better ask the service directly.
//NOTE: This was also used to recover from a service restart, but this data should not be exposed to the whole application.
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
@Deprecated //Use the TrackRecordingService
public static Track.Id getRecordingTrackId(Context context, SharedPreferences sharedPreferences) {
return new Track.Id(sharedPreferences.getLong(getKey(context, R.string.recording_track_id_key), RECORDING_TRACK_ID_DEFAULT));
}
@Deprecated //Use the TrackRecordingService
public static Track.Id getRecordingTrackId(SharedPreferences sharedPreferences, Context context) {
return getRecordingTrackId(context, sharedPreferences);
}
public static String getDefaultActivity(SharedPreferences sharedPreferences, Context context) {
return getString(sharedPreferences, context, R.string.default_activity_key, context.getString(R.string.default_activity_default));
}
@@ -95,14 +79,6 @@ public class PreferencesUtils {
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
}
//TODO Don't use; this function is only to be used TrackRecordingService and will be removed.
@VisibleForTesting
public static void setBoolean(SharedPreferences sharedPreferences, Context context, int keyId, boolean value) {
Editor editor = sharedPreferences.edit();
editor.putBoolean(getKey(context, keyId), value);
editor.apply();
}
private static int getInt(SharedPreferences sharedPreferences, Context context, int keyId, int defaultValue) {
try {
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
@@ -119,22 +95,6 @@ public class PreferencesUtils {
}
}
@VisibleForTesting
public static void setInt(SharedPreferences sharedPreferences, Context context, int keyId, int value) {
Editor editor = sharedPreferences.edit();
editor.putInt(getKey(context, keyId), value);
editor.apply();
}
//TODO Don't use; this function is only to be used TrackRecordingService and will be removed.
@Deprecated
@VisibleForTesting
public static void setLong(SharedPreferences sharedPreferences, Context context, int keyId, long value) {
Editor editor = sharedPreferences.edit();
editor.putLong(getKey(context, keyId), value);
editor.apply();
}
public static String getString(SharedPreferences sharedPreferences, Context context, int keyId, String defaultValue) {
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
}
@@ -161,22 +121,6 @@ public class PreferencesUtils {
return currentStatsRate.equals(context.getString(R.string.stats_rate_speed));
}
@Deprecated //Use TrackRecordingService
public static boolean isRecordingTrackPaused(SharedPreferences sharedPreferences, Context context) {
return getBoolean(sharedPreferences, context, R.string.recording_track_paused_key, isRecordingTrackPausedDefault(context));
}
@Deprecated //Use TrackRecordingService
public static boolean isRecordingTrackPausedDefault(Context context) {
return context.getResources().getBoolean(R.bool.recording_track_paused_default);
}
@Deprecated //Use TrackRecordingService
public static void defaultRecordingTrackPaused(SharedPreferences sharedPreferences, Context context) {
final boolean RECORDING_TRACK_PAUSED = context.getResources().getBoolean(R.bool.recording_track_paused_default);
setBoolean(sharedPreferences, context, R.string.recording_track_paused_key, RECORDING_TRACK_PAUSED);
}
private static String getBluetoothSensorAddressNone(Context context) {
return context.getString(R.string.sensor_type_value_none);
}
@@ -302,20 +246,6 @@ public class PreferencesUtils {
return Integer.parseInt(value);
}
@Deprecated //Use TrackRecordingService
public static boolean isRecording(SharedPreferences sharedPreferences, Context context) {
return isRecording(getRecordingTrackId(context, sharedPreferences));
}
@Deprecated
//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) {
if (recordingTrackId == null) {
return false;
}
return recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT;
}
public static SharedPreferences resetPreferences(Context context, boolean readAgain) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
if (readAgain) {
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<resources>
<!-- Keys for persisted preferences that are used by internally. -->
<!--But they should not get backed up or restored.-->
<string name="recording_track_id_key" translatable="false">recordingTrackId</string>
<string name="recording_track_paused_key" translatable="false">recordingTrackPaused</string>
<bool name="recording_track_paused_default" translatable="false">true</bool>
</resources>
+3
View File
@@ -31,6 +31,9 @@
<string name="post_workout_export_enabled_key" translatable="false">instantExportEnabled</string>
<bool name="post_workout_export_enabled_default" translatable="false">false</bool>
<string name="settings_import" translatable="false">settingsImport</string>
<string name="settings_export" translatable="false">settingsExport</string>
<string name="default_activity_key" translatable="false">defaultActivity</string>
<string name="default_activity_default" translatable="false">@string/activity_type_unknown</string>
<string-array name="activity_types">
+6 -2
View File
@@ -148,12 +148,16 @@ limitations under the License.
</PreferenceCategory>
<PreferenceCategory android:title="@string/generic_tracks">
<Preference android:title="@string/menu_import_all">
<Preference
android:key="@string/settings_import"
android:title="@string/menu_import_all">
<intent
android:targetClass="de.dennisguse.opentracks.io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity"
android:targetPackage="@string/applicationId" />
</Preference>
<Preference android:title="@string/menu_export_all">
<Preference
android:key="@string/settings_export"
android:title="@string/menu_export_all">
<intent
android:targetClass="de.dennisguse.opentracks.io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity"
android:targetPackage="@string/applicationId" />