From 4f31607e3e57aa111137d434501eeff9be328a74 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 26 Aug 2021 16:25:44 +0200 Subject: [PATCH] Voice announcements are now triggered by the TrackRecordingService's timer that is also used for UI updates. --- ...java => VoiceAnnouncementManagerTest.java} | 12 +- .../services/TrackRecordingService.java | 34 ++--- .../services/tasks/PeriodicTaskFactory.java | 59 --------- ...riodicTask.java => VoiceAnnouncement.java} | 39 ++---- ...tor.java => VoiceAnnouncementManager.java} | 120 +++++++----------- 5 files changed, 72 insertions(+), 192 deletions(-) rename src/androidTest/java/de/dennisguse/opentracks/services/tasks/{PeriodicTaskExecutorTest.java => VoiceAnnouncementManagerTest.java} (80%) delete mode 100644 src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskFactory.java rename src/main/java/de/dennisguse/opentracks/services/tasks/{AnnouncementPeriodicTask.java => VoiceAnnouncement.java} (85%) rename src/main/java/de/dennisguse/opentracks/services/tasks/{PeriodicTaskExecutor.java => VoiceAnnouncementManager.java} (55%) diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutorTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManagerTest.java similarity index 80% rename from src/androidTest/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutorTest.java rename to src/androidTest/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManagerTest.java index 0f54013f6..582c55986 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutorTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManagerTest.java @@ -25,7 +25,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics; import static org.junit.Assert.assertEquals; @RunWith(AndroidJUnit4.class) -public class PeriodicTaskExecutorTest { +public class VoiceAnnouncementManagerTest { @Rule public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS); @@ -53,17 +53,17 @@ public class PeriodicTaskExecutorTest { TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class))) .getService(); - PeriodicTaskExecutor periodicTaskExecutor = new PeriodicTaskExecutor(service, new AnnouncementPeriodicTask.Factory()); - periodicTaskExecutor.setMetricUnits(true); - periodicTaskExecutor.setTaskFrequency(-5); + VoiceAnnouncementManager voiceAnnouncementManager = new VoiceAnnouncementManager(service); + voiceAnnouncementManager.setMetricUnits(true); + voiceAnnouncementManager.setTaskFrequency(-5); // when TrackStatistics statistics = new TrackStatistics(); statistics.setTotalDistance(Distance.of(13000)); - assertEquals(Distance.of(15000), periodicTaskExecutor.calculateNextTaskDistance(statistics)); + assertEquals(Distance.of(15000), voiceAnnouncementManager.calculateNextTaskDistance(statistics)); statistics.setTotalDistance(Distance.of(15100)); - assertEquals(Distance.of(20000), periodicTaskExecutor.calculateNextTaskDistance(statistics)); + assertEquals(Distance.of(20000), voiceAnnouncementManager.calculateNextTaskDistance(statistics)); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 5b2d0cc1b..cdefbef8b 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -49,9 +49,7 @@ import de.dennisguse.opentracks.io.file.exporter.ExportServiceResultReceiver; import de.dennisguse.opentracks.services.handlers.EGM2008CorrectionManager; import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; -import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTask; -import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor; -import de.dennisguse.opentracks.services.tasks.PeriodicTaskFactory; +import de.dennisguse.opentracks.services.tasks.VoiceAnnouncementManager; import de.dennisguse.opentracks.settings.SettingsActivity; import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.util.ExportUtils; @@ -76,7 +74,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. public static final GpsStatusValue STATUS_GPS_DEFAULT = GpsStatusValue.GPS_NONE; // The following variables are set in onCreate: - private PeriodicTaskExecutor voiceExecutor; + private VoiceAnnouncementManager voiceAnnouncementManager; private TrackRecordingServiceNotificationManager notificationManager; private TrackRecordingManager trackRecordingManager; @@ -106,11 +104,11 @@ public class TrackRecordingService extends Service implements TrackPointCreator. Context context = TrackRecordingService.this; if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) { boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context); - voiceExecutor.setMetricUnits(metricUnits); + voiceAnnouncementManager.setMetricUnits(metricUnits); notificationManager.setMetricUnits(metricUnits); } if (PreferencesUtils.isKey(context, R.string.voice_frequency_key, key)) { - voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(sharedPreferences, context)); + voiceAnnouncementManager.setTaskFrequency(PreferencesUtils.getVoiceFrequency(sharedPreferences, context)); } @@ -145,7 +143,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. trackRecordingManager = new TrackRecordingManager(this); handlerServer = new TrackPointCreator(this); - voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTask.Factory()); + voiceAnnouncementManager = new VoiceAnnouncementManager(this); notificationManager = new TrackRecordingServiceNotificationManager(this); @@ -178,9 +176,9 @@ public class TrackRecordingService extends Service implements TrackPointCreator. sharedPreferences = null; try { - voiceExecutor.shutdown(); + voiceAnnouncementManager.shutdown(); } finally { - voiceExecutor = null; + voiceAnnouncementManager = null; } // This should be the next to last operation @@ -209,10 +207,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator. return trackRecordingManager.insertMarker(name, category, description, photoUrl); } - public void run(@NonNull PeriodicTaskFactory.Task periodicTask) { - periodicTask.run(recordingStatus.getTrackId(), trackRecordingManager.getTrackStatistics()); - } - /** * Starts a new track. * @@ -267,8 +261,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. startGps(); - // Restore periodic tasks - voiceExecutor.restore(); + voiceAnnouncementManager.restore(trackRecordingManager.getTrackStatistics()); } public void tryStartGps() { @@ -337,7 +330,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. } // Shutdown periodic tasks - voiceExecutor.shutdown(); + voiceAnnouncementManager.shutdown(); // Update instance variables handlerServer.stop(); @@ -374,7 +367,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator. trackRecordingManager.onNewTrackPoint(trackPoint, thresholdHorizontalAccuracy); notificationManager.updateTrackPoint(this, trackRecordingManager.getTrackStatistics(), trackPoint, thresholdHorizontalAccuracy); - voiceExecutor.update(recordingStatus.getTrackId(), trackRecordingManager.getTrackStatistics()); } @Override @@ -416,10 +408,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator. } } - public Duration getTotalTime() { - return trackRecordingManager.getTrackStatistics().getTotalTime(); - } - @Deprecated @VisibleForTesting public TrackPointCreator getHandlerServer() { @@ -430,7 +418,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. return gpsStatusObservable; } - public MutableLiveData getRecordingDataObservable() { + public LiveData getRecordingDataObservable() { return recordingDataObservable; } @@ -457,6 +445,8 @@ public class TrackRecordingService extends Service implements TrackPointCreator. TrackPoint trackPoint = data.second.first; egm2008CorrectionManager.correctAltitude(this, trackPoint); + voiceAnnouncementManager.update(data.first); + recordingDataObservable.postValue(new RecordingData(data.first, trackPoint, data.second.second)); } diff --git a/src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskFactory.java b/src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskFactory.java deleted file mode 100644 index cfaee4525..000000000 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package de.dennisguse.opentracks.services.tasks; - -import android.content.Context; - -import androidx.annotation.NonNull; - -import de.dennisguse.opentracks.content.data.Track; -import de.dennisguse.opentracks.stats.TrackStatistics; - -/** - * An interface for classes that can create {@link Task}. - * - * @author Sandor Dornbush - */ -public interface PeriodicTaskFactory { - - @NonNull - Task create(Context context); - - /** - * This is interface for a task that will be executed on some schedule. - * - * @author Sandor Dornbush - */ - interface Task { - - /** - * Sets up this task for subsequent calls to the run method. - */ - void start(); - - /** - * This method will be called periodically. - */ - @Deprecated - void run(@NonNull Track.Id trackId, @NonNull TrackStatistics trackStatistics); - - /** - * Shuts down this task and clean up resources. - */ - void shutdown(); - } -} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java b/src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncement.java similarity index 85% rename from src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java rename to src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncement.java index 3c1092845..c1aee351d 100644 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java +++ b/src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncement.java @@ -38,13 +38,13 @@ import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; /** - * This class will periodically announce the user's {@link TrackStatistics}. + * This class will announce the user's {@link TrackStatistics}. * * @author Sandor Dornbush */ -public class AnnouncementPeriodicTask implements PeriodicTaskFactory.Task { +public class VoiceAnnouncement { - private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName(); + private static final String TAG = VoiceAnnouncement.class.getSimpleName(); private final Context context; @@ -107,14 +107,13 @@ public class AnnouncementPeriodicTask implements PeriodicTaskFactory.Task { private boolean ttsReady = false; - AnnouncementPeriodicTask(Context context) { + VoiceAnnouncement(Context context) { this.context = context; audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); contentProviderUtils = new ContentProviderUtils(context); } - @Override public void start() { Log.d(TAG, "Start"); @@ -128,8 +127,7 @@ public class AnnouncementPeriodicTask implements PeriodicTaskFactory.Task { } } - @Override - public void run(@NonNull Track.Id trackId, @NonNull TrackStatistics trackStatistics) { + public void announce(@NonNull Track track) { synchronized (this) { if (!ttsReady) { ttsReady = ttsInitStatus == TextToSpeech.SUCCESS; @@ -149,27 +147,20 @@ public class AnnouncementPeriodicTask implements PeriodicTaskFactory.Task { return; } - Track track = contentProviderUtils.getTrack(trackId); - if (track == null) { - Log.i(TAG, "It doesn't exists a track with trackid = " + track); - return; - } - String category = track.getCategory(); - boolean isMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context); - boolean isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category); + boolean isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, track.getCategory()); Distance minGPSDistance = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context); + //TODO Do not load all trackpoints for every announcement TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null); IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.one(isMetricUnits), minGPSDistance); intervalStatistics.addTrackPoints(trackPointIterator); IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval(); - String announcement = AnnouncementUtils.getAnnouncement(context, trackStatistics, isMetricUnits, isReportSpeed, lastInterval); + String announcement = AnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), isMetricUnits, isReportSpeed, lastInterval); speakAnnouncement(announcement); } - @Override public void shutdown() { if (tts != null) { tts.shutdown(); @@ -199,18 +190,4 @@ public class AnnouncementPeriodicTask implements PeriodicTaskFactory.Task { // We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called. tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used"); } - - /** - * A {@link PeriodicTaskFactory} for text-to-speech announcement periodic task. - * - * @author Rodrigo Damazio - */ - public static class Factory implements PeriodicTaskFactory { - - @Override - @NonNull - public Task create(Context context) { - return new AnnouncementPeriodicTask(context); - } - } } diff --git a/src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutor.java b/src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManager.java similarity index 55% rename from src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutor.java rename to src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManager.java index eff52d63b..3be013d0c 100644 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/PeriodicTaskExecutor.java +++ b/src/main/java/de/dennisguse/opentracks/services/tasks/VoiceAnnouncementManager.java @@ -15,7 +15,6 @@ */ package de.dennisguse.opentracks.services.tasks; -import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; @@ -34,12 +33,11 @@ import de.dennisguse.opentracks.stats.TrackStatistics; * * @author Sandor Dornbush */ -public class PeriodicTaskExecutor { +public class VoiceAnnouncementManager { - private static final String TAG = PeriodicTaskExecutor.class.getSimpleName(); + private static final String TAG = VoiceAnnouncementManager.class.getSimpleName(); private final TrackRecordingService trackRecordingService; - private final PeriodicTaskFactory periodicTaskFactory; private final int TASK_FREQUENCY_OFF; /** @@ -50,102 +48,76 @@ public class PeriodicTaskExecutor { */ private int taskFrequency; - private PeriodicTaskFactory.Task periodicTask; - - private Handler handler; + private VoiceAnnouncement voiceAnnouncement; private boolean metricUnits; private TrackStatistics trackStatistics; - private Distance nextTaskDistance = Distance.of(Double.MAX_VALUE); + private Distance nextTotalDistance = Distance.of(Double.MAX_VALUE); + private Duration nextTotalTime = Duration.ofSeconds(Long.MAX_VALUE); - private final Runnable timer = new Runnable() { - @Override - public void run() { - if (trackRecordingService != null && periodicTask != null) { - trackRecordingService.run(periodicTask); - handler.postDelayed(this, getNextDuration()); - } - } - }; - - public PeriodicTaskExecutor(@NonNull TrackRecordingService trackRecordingService, PeriodicTaskFactory periodicTaskFactory) { + public VoiceAnnouncementManager(@NonNull TrackRecordingService trackRecordingService) { this.trackRecordingService = trackRecordingService; - this.periodicTaskFactory = periodicTaskFactory; TASK_FREQUENCY_OFF = Integer.parseInt(trackRecordingService.getString(R.string.frequency_off)); taskFrequency = TASK_FREQUENCY_OFF; } - public void restore() { - if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) { - Log.d(TAG, "Not recording or paused."); - return; - } - - if (!isTimeFrequency() && handler != null) { - handler.removeCallbacks(timer); - handler = null; - } + public void restore(TrackStatistics trackStatistics) { if (taskFrequency == TASK_FREQUENCY_OFF) { Log.d(TAG, "Task frequency is off."); return; } - periodicTask = periodicTaskFactory.create(trackRecordingService); - periodicTask.start(); + voiceAnnouncement = new VoiceAnnouncement(trackRecordingService); + voiceAnnouncement.start(); if (isTimeFrequency()) { - if (handler == null) { - handler = new Handler(); - } - handler.postDelayed(timer, getNextDuration()); + nextTotalTime = calculateNextDuration(trackStatistics); } else { // For distance periodic task - updateNextTaskDistance(); + updateNextTaskDistance(trackStatistics); + } + } + + public void update(@NonNull Track track) { + boolean announce = false; + this.trackStatistics = track.getTrackStatistics(); + if (trackStatistics.getTotalDistance().greaterThan(nextTotalDistance)) { + updateNextTaskDistance(trackStatistics); + announce = true; + } + if (!trackStatistics.getTotalTime().minus(nextTotalTime).isNegative()) { + nextTotalTime = calculateNextDuration(trackStatistics); + announce = true; + } + + if (announce) { + this.trackStatistics = track.getTrackStatistics(); + voiceAnnouncement.announce(track); } } public void shutdown() { - if (periodicTask != null) { - periodicTask.shutdown(); - periodicTask = null; - } - if (handler != null) { - handler.removeCallbacks(timer); - handler = null; - } - } - - public void update(@NonNull Track.Id trackId, @NonNull TrackStatistics trackStatistics) { - if (!isDistanceFrequency() || periodicTask == null) { - return; - } - - if (trackStatistics.getTotalDistance().greaterThan(nextTaskDistance)) { - periodicTask.run(trackId, trackStatistics); - this.trackStatistics = trackStatistics; - updateNextTaskDistance(); + if (voiceAnnouncement != null) { + voiceAnnouncement.shutdown(); + voiceAnnouncement = null; } } public void setTaskFrequency(int taskFrequency) { this.taskFrequency = taskFrequency; - restore(); + restore(this.trackStatistics); } public void setMetricUnits(boolean metricUnits) { this.metricUnits = metricUnits; - updateNextTaskDistance(); + updateNextTaskDistance(this.trackStatistics); } - private void updateNextTaskDistance() { - if (!trackRecordingService.isRecording() || trackRecordingService.isPaused() || periodicTask == null) { - return; - } - + private void updateNextTaskDistance(TrackStatistics trackStatistics) { if (!isDistanceFrequency()) { - nextTaskDistance = Distance.of(Double.MAX_VALUE); + nextTotalDistance = Distance.of(Double.MAX_VALUE); Log.d(TAG, "SplitManager: Distance splits disabled."); return; } @@ -154,11 +126,11 @@ public class PeriodicTaskExecutor { return; } - nextTaskDistance = calculateNextTaskDistance(trackStatistics); + nextTotalDistance = calculateNextTaskDistance(trackStatistics); } @VisibleForTesting - public Distance calculateNextTaskDistance(TrackStatistics trackStatistics) { + public Distance calculateNextTaskDistance(@NonNull TrackStatistics trackStatistics) { Distance distance = trackStatistics.getTotalDistance(); Distance announcementInterval = Distance.one(metricUnits).multipliedBy(Math.abs(taskFrequency)); @@ -166,6 +138,14 @@ public class PeriodicTaskExecutor { return announcementInterval.multipliedBy(index + 1); } + private Duration calculateNextDuration(@NonNull TrackStatistics trackStatistics) { + if (!isTimeFrequency()) { + throw new RuntimeException("Using distance frequency as time frequency is impossible."); + } + Duration interval = Duration.ofMinutes(taskFrequency); + return interval.minus(Duration.ofMillis(trackStatistics.getTotalTime().toMillis() % interval.toMillis())); + } + private boolean isTimeFrequency() { return taskFrequency > 0; } @@ -173,12 +153,4 @@ public class PeriodicTaskExecutor { private boolean isDistanceFrequency() { return taskFrequency < 0; } - - private long getNextDuration() { - if (!isTimeFrequency()) { - throw new RuntimeException("Using distance frequency as time frequency is impossible."); - } - Duration interval = Duration.ofMinutes(taskFrequency); - return interval.toMillis() - (trackRecordingService.getTotalTime().toMillis() % interval.toMillis()); - } }