diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java index 345048207..a19bb0a85 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java @@ -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)); } /** diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 66128021a..6c63ed9d1 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -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); diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java index 233b5d340..643c23737 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java @@ -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 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 trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId); assertEquals(4, trackPoints.size()); @@ -285,7 +285,6 @@ public class TrackRecordingServiceTest { // then assertFalse(service.isRecording()); - assertNull(service.getRecordingTrackId()); List 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 { diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLooper.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLooper.java index d44f84bfa..49b5613bd 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLooper.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLooper.java @@ -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()); diff --git a/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java index e63be345b..65716774b 100644 --- a/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java @@ -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))); diff --git a/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java b/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java index 71d706074..2080610c7 100644 --- a/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java @@ -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; + } } diff --git a/src/main/java/de/dennisguse/opentracks/Startup.java b/src/main/java/de/dennisguse/opentracks/Startup.java index 4f7c41f8b..8362f297f 100644 --- a/src/main/java/de/dennisguse/opentracks/Startup.java +++ b/src/main/java/de/dennisguse/opentracks/Startup.java @@ -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(); diff --git a/src/main/java/de/dennisguse/opentracks/TrackController.java b/src/main/java/de/dennisguse/opentracks/TrackController.java index 8c50b71a1..01842abde 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackController.java +++ b/src/main/java/de/dennisguse/opentracks/TrackController.java @@ -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(); } } diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index 96e1a3789..3c06a0eed 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -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; } } diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java index f5f7f4b88..7e1cc1141 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java @@ -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; + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index f7e8b3651..c06769276 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -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); + } + } } diff --git a/src/main/java/de/dennisguse/opentracks/content/TrackDataHub.java b/src/main/java/de/dennisguse/opentracks/content/TrackDataHub.java index 072c31e0b..6dbec0f2c 100644 --- a/src/main/java/de/dennisguse/opentracks/content/TrackDataHub.java +++ b/src/main/java/de/dennisguse/opentracks/content/TrackDataHub.java @@ -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; + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java index 7df2d9198..93984b982 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java @@ -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) { diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java index 39725f0ce..0290d4300 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java @@ -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 parseKml(ZipInputStream zipInputStream) { KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 3fe35e102..144c00f6e 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -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 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) diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java index 3cc7be682..03f977241 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java @@ -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(); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceCallback.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceCallback.java deleted file mode 100644 index bef363c31..000000000 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceCallback.java +++ /dev/null @@ -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); -} diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java index bfa67c665..21b2e4c06 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java @@ -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); } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java index 99486c79c..84b04c477 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java @@ -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(); } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceStatus.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceStatus.java new file mode 100644 index 000000000..c38cb6f80 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceStatus.java @@ -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 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) {} + } +} diff --git a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java b/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java index be13f0888..b913d4535 100644 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java +++ b/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java @@ -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. diff --git a/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java b/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java index aab08c047..1a7586736 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java +++ b/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java @@ -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(); + } + } } } diff --git a/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java index 51bf23509..d7ab479fd 100644 --- a/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java @@ -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) { diff --git a/src/main/res/values/preferences_internal.xml b/src/main/res/values/preferences_internal.xml deleted file mode 100644 index 9653c35b7..000000000 --- a/src/main/res/values/preferences_internal.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - recordingTrackId - - recordingTrackPaused - true - \ No newline at end of file diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index ad77abd22..ffaf84cb7 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -31,6 +31,9 @@ instantExportEnabled false + settingsImport + settingsExport + defaultActivity @string/activity_type_unknown diff --git a/src/main/res/xml/settings.xml b/src/main/res/xml/settings.xml index e792e4d08..0eaace250 100644 --- a/src/main/res/xml/settings.xml +++ b/src/main/res/xml/settings.xml @@ -148,12 +148,16 @@ limitations under the License. - + - +