diff --git a/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java b/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java index 8b0b63420..50ffbc9fa 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java @@ -21,11 +21,13 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Build; import android.os.Bundle; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; @@ -43,6 +45,7 @@ import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.StatsFragment; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; +import de.dennisguse.opentracks.services.TrackRecordingServiceInterface; import de.dennisguse.opentracks.settings.SettingsActivity; import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.PreferencesUtils; @@ -50,7 +53,7 @@ import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackUtils; /** - * An activity to show the track detail. + * An activity to show the track detail, record a new track or resumes an existing one. * * @author Leif Hendrik Wilden * @author Rodrigo Damazio @@ -89,6 +92,34 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA trackController.update(isRecording(), recordingTrackPaused); } }); + + if (recordingTrackId == -1L) { + TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound(); + if (service == null) { + Log.d(TAG, "it could not get service"); + return; + } + + // Starts or resumes a track. + int msg; + if (trackId == -1L) { + // trackId isn't initialized -> leads a new recording. + trackId = service.startNewTrack(); + recordingTrackId = trackId; + msg = R.string.track_detail_record_success; + } else { + // trackId is initialized -> resumes the track. + recordingTrackId = trackId; + service.resumeTrack(trackId); + msg = R.string.track_detail_resume_success; + } + + // A recording track is on. + Toast.makeText(TrackDetailActivity.this, msg, Toast.LENGTH_SHORT).show(); + trackDataHub.loadTrack(trackId); + trackController.update(true, false); + trackController.onResume(true, recordingTrackPaused); + } } }; @@ -131,6 +162,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA private MenuItem insertMarkerMenuItem; private MenuItem markerListMenuItem; private MenuItem shareMenuItem; + private MenuItem resumeMenuItem; private final OnClickListener recordListener = new OnClickListener() { @Override @@ -258,11 +290,16 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA @Override protected void onResume() { super.onResume(); - trackDataHub.loadTrack(trackId); // Update UI this.invalidateOptionsMenu(); - trackController.onResume(isRecording(), recordingTrackPaused); + + if (trackId != -1L) { + trackDataHub.loadTrack(trackId); + trackController.onResume(isRecording(), recordingTrackPaused); + } else { + startRecording(); + } } @Override @@ -304,6 +341,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker); shareMenuItem = menu.findItem(R.id.track_detail_share); markerListMenuItem = menu.findItem(R.id.track_detail_markers); + resumeMenuItem = menu.findItem(R.id.track_detail_resume_track); return super.onCreateOptionsMenu(menu); } @@ -345,6 +383,9 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA case R.id.track_detail_delete: deleteTracks(new long[]{trackId}); return true; + case R.id.track_detail_resume_track: + startRecording(); + return true; case R.id.track_detail_settings: intent = IntentUtils.newIntent(this, SettingsActivity.class); startActivity(intent); @@ -387,9 +428,13 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA return; } trackId = waypoint.getTrackId(); + if (trackId == -1L) { + finish(); + return; + } } + if (trackId == -1L) { - finish(); return; } Track track = contentProviderUtils.getTrack(trackId); @@ -416,9 +461,12 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA String title; if (isRecording()) { title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording); + resumeMenuItem.setVisible(false); } else { Track track = contentProviderUtils.getTrack(trackId); title = track != null ? track.getName() : ""; + // Only visible if there aren't other tracks that is recording. + resumeMenuItem.setVisible(recordingTrackId == -1L); } setTitle(title); } @@ -437,4 +485,15 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA private boolean isRecording() { return trackId == recordingTrackId; } + + private void startRecording() { + trackRecordingServiceConnection.startAndBind(this); + + /* + * If the binding has happened, then invoke the callback to start a new recording. + * If the binding hasn't happened, then invoking the callback will have no effect. + * But when the binding occurs, the callback will get invoked. + */ + bindChangedCallback.run(); + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index 8b2a73f48..85dedbd29 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -160,7 +160,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe // Menu items private MenuItem searchMenuItem; private MenuItem startGpsMenuItem; -// private MenuItem deleteAllMenuItem; private final OnClickListener stopListener = new OnClickListener() { @Override @@ -172,8 +171,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe private boolean startGps = false; // true to start gps - private boolean startNewRecording = false; // true to start a new recording - // Callback when the trackRecordingServiceConnection binding changes. private final Runnable bindChangedCallback = new Runnable() { @Override @@ -188,7 +185,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe } }); - if (!startGps && !startNewRecording) { + if (!startGps) { return; } @@ -197,17 +194,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe Log.d(TAG, "service not available to start gps or a new recording"); return; } - if (startNewRecording) { - startGps = false; - - long trackId = service.startNewTrack(); - startNewRecording = false; - Intent intent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class) - .putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId); - startActivity(intent); - Toast.makeText(TrackListActivity.this, R.string.track_list_record_success, Toast.LENGTH_SHORT) - .show(); - } if (startGps) { service.startGps(); startGps = false; @@ -220,18 +206,19 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe if (!PreferencesUtils.isRecording(recordingTrackId)) { // Not recording -> Recording updateMenuItems(false, true); - startRecording(); + Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class); + startActivity(newIntent); } else if (recordingTrackPaused) { - // Paused -> Resume - updateMenuItems(false, true); - trackRecordingServiceConnection.resumeTrack(); - trackController.update(true, false); - } else { - // Recording -> Paused - updateMenuItems(false, true); - trackRecordingServiceConnection.pauseTrack(); - trackController.update(true, true); - } + // Paused -> Resume + updateMenuItems(false, true); + trackRecordingServiceConnection.resumeTrack(); + trackController.update(true, false); + } else { + // Recording -> Paused + updateMenuItems(false, true); + trackRecordingServiceConnection.pauseTrack(); + trackController.update(true, true); + } } }; @@ -489,21 +476,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe } } - /** - * Starts a new recording. - */ - private void startRecording() { - startNewRecording = true; - trackRecordingServiceConnection.startAndBind(this); - - /* - * If the binding has happened, then invoke the callback to start a new recording. - * If the binding hasn't happened, then invoking the callback will have no effect. - * But when the binding occurs, the callback will get invoked. - */ - bindChangedCallback.run(); - } - /** * Handles a context item selection. * diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java index 207c6babf..963997968 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java @@ -96,6 +96,16 @@ public class StatsFragment extends Fragment implements TrackDataListener { } if (PreferencesUtils.isKey(getContext(), R.string.recording_track_id_key, key)) { recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(getContext()); + if (PreferencesUtils.getRecordingTrackId(getContext()) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { + // A recording track id has been set -> Resumes track and starts timer. + resumeTrackDataHub(); + if (trackRecordingServiceConnection == null) { + trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null); + } + trackRecordingServiceConnection.startConnection(getContext()); + + handlerUpdateUI.post(updateUIeachSecond); + } } } }; @@ -533,13 +543,26 @@ public class StatsFragment extends Fragment implements TrackDataListener { private void updateTotalTime() { long totalTime; if (isSelectedTrackRecording()) { - totalTime = System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime(); + totalTime = calculateTotalTime(); } else { totalTime = lastTripStatistics.getTotalTime(); } totalTimeValueView.setText(StringUtils.formatElapsedTime(totalTime)); } + /** + * Return time from service. + * If service isn't bound then use lastTripStatistics for calculate it. + */ + private long calculateTotalTime() { + TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound(); + if (trackRecordingService != null) { + return trackRecordingService.getTotalTime(); + } else { + return System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime(); + } + } + private void setLocationValues() { boolean metricUnits = PreferencesUtils.isMetricUnits(getContext()); boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext()); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 9c9b35cb7..f77670f29 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -352,6 +352,27 @@ public class TrackRecordingService extends Service { return trackId; } + /** + * Resumes the track identified by trackId. + * + * @param trackId + */ + void resumeTrack(long trackId) { + Track track = contentProviderUtils.getTrack(trackId); + if (track == null) { + Log.e(TAG, "Ignore resumeTrack. Track " + trackId + " does not exists."); + return; + } + + track.getTripStatistics().setStopTime(System.currentTimeMillis()); + trackTripStatisticsUpdater = new TripStatisticsUpdater(track.getTripStatistics()); + + // Update shared preferences + updateRecordingState(trackId, false); + + startRecording(); + } + private void restartTrack(Track track) { Log.d(TAG, "Restarting track: " + track.getId()); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java index 12b63ed4e..19eb2723e 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java @@ -36,6 +36,11 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco return trackRecordingService.startNewTrack(); } + @Override + public void resumeTrack(long trackId) { + trackRecordingService.resumeTrack(trackId); + } + @Override public void pauseCurrentTrack() { trackRecordingService.pauseCurrentTrack(); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java index b3915772f..c7e01191f 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java @@ -44,6 +44,13 @@ public interface TrackRecordingServiceInterface { */ long startNewTrack(); + /** + * Resumes the track identified by trackId. + * + * @param trackId + */ + void resumeTrack(long trackId); + /** * Pauses the current recording track. */ diff --git a/src/main/java/de/dennisguse/opentracks/stats/TripStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/TripStatisticsUpdater.java index c51ba6652..2900d0979 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TripStatisticsUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TripStatisticsUpdater.java @@ -96,6 +96,16 @@ public class TripStatisticsUpdater { currentSegment = init(startTime); } + /** + * Creates a new trip statistics updater with a trip statistics already existed. + * + * @param tripStatistics a trip statistics. + */ + public TripStatisticsUpdater(TripStatistics tripStatistics) { + this.tripStatistics = tripStatistics; + currentSegment = init(System.currentTimeMillis()); + } + public void updateTime(long time) { currentSegment.setStopTime(time); currentSegment.setTotalTime(time - currentSegment.getStartTime()); diff --git a/src/main/res/drawable/ic_play_circle_24dp.xml b/src/main/res/drawable/ic_play_circle_24dp.xml new file mode 100644 index 000000000..930ee0ed5 --- /dev/null +++ b/src/main/res/drawable/ic_play_circle_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/menu/track_detail.xml b/src/main/res/menu/track_detail.xml index b3ae34f7e..3fd5d24a1 100644 --- a/src/main/res/menu/track_detail.xml +++ b/src/main/res/menu/track_detail.xml @@ -48,6 +48,11 @@ limitations under the License. android:icon="@drawable/ic_delete_forever_24dp" android:orderInCategory="10" android:title="@string/menu_delete" /> + Detener GPS Ver foto Frecuencia de voz + Reanudar ruta OpenTracks Servicio de grabación Recorridos y marcadores @@ -299,6 +300,7 @@ Eliminando… Gráfico Estadísticas + Grabando una nueva ruta Tipo de actividad (p. ej., senderismo) Tipo de actividad Nuevo recorrido diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 0031dc5f8..7496c2245 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -211,6 +211,7 @@ Detener GPS Ver foto Frecuencia de voz + Reanudar ruta OpenTracks Servicio de grabación Recorridos y marcadores @@ -303,6 +304,7 @@ Eliminando… Gráfico Estadísticas + Grabando una nueva ruta Tipo de actividad (p. ej., senderismo…) Tipo de actividad Nueva pista diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7a2a264fc..d97edbf73 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -321,6 +321,7 @@ limitations under the License. Stop GPS View photo Voice frequency + Resume track OpenTracks Recording service @@ -440,6 +441,8 @@ limitations under the License. Chart Stats + Now recording a new track + Now resuming the track Activity type (e.g. hiking) Activity type