From 83da4f8f347efc74719319f0f19b7e9f1c9c8b0c Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 23 Sep 2023 18:52:28 +0200 Subject: [PATCH] Idle: add voice announcement (user configurable). Fixes of #1174 --- .../VoiceAnnouncementUtilsTest.java | 32 ++++----- .../opentracks/AbstractActivity.java | 4 +- .../services/TrackRecordingManager.java | 11 ++- .../services/TrackRecordingService.java | 10 ++- ...VoiceAnnouncement.java => TTSManager.java} | 48 ++----------- .../VoiceAnnouncementManager.java | 72 ++++++++++++++++--- .../announcement/VoiceAnnouncementUtils.java | 7 +- .../AnnouncementsSettingsFragment.java | 2 +- .../opentracks/settings/PreferencesUtils.java | 4 ++ src/main/res/values-b+es+419/strings.xml | 2 +- src/main/res/values-cs/strings.xml | 2 +- src/main/res/values-da/strings.xml | 2 +- src/main/res/values-de/strings.xml | 2 +- src/main/res/values-es/strings.xml | 2 +- src/main/res/values-et/strings.xml | 2 +- src/main/res/values-eu/strings.xml | 2 +- src/main/res/values-fi/strings.xml | 2 +- src/main/res/values-fr/strings.xml | 2 +- src/main/res/values-ga/strings.xml | 2 +- src/main/res/values-gl/strings.xml | 2 +- src/main/res/values-hu/strings.xml | 2 +- src/main/res/values-it/strings.xml | 2 +- src/main/res/values-nb/strings.xml | 2 +- src/main/res/values-nl/strings.xml | 2 +- src/main/res/values-pl/strings.xml | 2 +- src/main/res/values-pt-rBR/strings.xml | 2 +- src/main/res/values-pt-rPT/strings.xml | 2 +- src/main/res/values-pt/strings.xml | 2 +- src/main/res/values-ru/strings.xml | 2 +- src/main/res/values-sk/strings.xml | 2 +- src/main/res/values-sl/strings.xml | 2 +- src/main/res/values-tr/strings.xml | 2 +- src/main/res/values-uk/strings.xml | 2 +- src/main/res/values-vi/strings.xml | 2 +- src/main/res/values-zh-rTW/strings.xml | 2 +- src/main/res/values-zh/strings.xml | 2 +- src/main/res/values/settings.xml | 10 +++ src/main/res/values/strings.xml | 7 +- src/main/res/xml/settings.xml | 2 +- src/main/res/xml/settings_announcements.xml | 61 +++++++++------- 40 files changed, 192 insertions(+), 132 deletions(-) rename src/main/java/de/dennisguse/opentracks/services/announcement/{VoiceAnnouncement.java => TTSManager.java} (73%) diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtilsTest.java index ab805fad6..aac0d6b10 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtilsTest.java @@ -63,7 +63,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString(); // then assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement); @@ -79,7 +79,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString(); // then assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement); @@ -95,7 +95,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString(); // then assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement); @@ -111,7 +111,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString(); // then assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement); @@ -133,7 +133,7 @@ public class VoiceAnnouncementUtilsTest { } // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString(); // then assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement); @@ -149,7 +149,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, null, null).toString(); // then assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement); @@ -171,7 +171,7 @@ public class VoiceAnnouncementUtilsTest { } // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString(); // then assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement); @@ -187,7 +187,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString(); // then assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement); @@ -201,7 +201,7 @@ public class VoiceAnnouncementUtilsTest { stats.setMovingTime(Duration.ofHours(1)); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString(); // then assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement); @@ -215,7 +215,7 @@ public class VoiceAnnouncementUtilsTest { stats.setMovingTime(Duration.ofHours(1)); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString(); // then assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement); @@ -229,7 +229,7 @@ public class VoiceAnnouncementUtilsTest { stats.setMovingTime(Duration.ofHours(1)); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString(); // then assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement); @@ -251,7 +251,7 @@ public class VoiceAnnouncementUtilsTest { } // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString(); // then assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement); @@ -267,7 +267,7 @@ public class VoiceAnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString(); // then assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement); @@ -289,7 +289,7 @@ public class VoiceAnnouncementUtilsTest { } // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString(); // then assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement); @@ -315,7 +315,7 @@ public class VoiceAnnouncementUtilsTest { SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString(); // then assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement); @@ -345,7 +345,7 @@ public class VoiceAnnouncementUtilsTest { SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null); // when - String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString(); + String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString(); // then assertEquals(" Current heart rate 133 bpm.", announcement); diff --git a/src/main/java/de/dennisguse/opentracks/AbstractActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractActivity.java index 169b55915..aae4c9b7d 100644 --- a/src/main/java/de/dennisguse/opentracks/AbstractActivity.java +++ b/src/main/java/de/dennisguse/opentracks/AbstractActivity.java @@ -21,7 +21,7 @@ import android.view.View; import androidx.appcompat.app.AppCompatActivity; -import de.dennisguse.opentracks.services.announcement.VoiceAnnouncement; +import de.dennisguse.opentracks.services.announcement.TTSManager; /** * @author Jimmy Shih @@ -33,7 +33,7 @@ public abstract class AbstractActivity extends AppCompatActivity { super.onCreate(savedInstanceState); // Set volume control stream for text to speech - setVolumeControlStream(VoiceAnnouncement.AUDIO_STREAM); + setVolumeControlStream(TTSManager.AUDIO_STREAM); setContentView(getRootView()); } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java index 50e8eb363..c0b299e8c 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java @@ -40,6 +40,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen private final ContentProviderUtils contentProviderUtils; private final Context context; + private final IdleObserver idleObserver; private final Handler handler; @@ -59,8 +60,9 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen private TrackPoint lastStoredTrackPoint; private TrackPoint lastStoredTrackPointWithLocation; - TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, Handler handler) { + TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, IdleObserver idleObserver, Handler handler) { this.context = context; + this.idleObserver = idleObserver; this.trackPointCreator = trackPointCreator; this.handler = handler; contentProviderUtils = new ContentProviderUtils(context); @@ -165,6 +167,8 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen public void onIdle() { Log.d(TAG, "Becoming idle"); onNewTrackPoint(trackPointCreator.createIdle()); + + idleObserver.onIdle(); } /** @@ -270,6 +274,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen lastStoredTrackPointWithLocation = lastStoredTrackPoint; } } catch (SQLiteException e) { + // TODO Remove; if this is a problem; use a synchronized method. /* * Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY). * This is expected to happen extremely rarely (if our listener gets invoked twice at about the same time). @@ -299,4 +304,8 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen idleDuration = PreferencesUtils.getIdleDurationTimeout(); } } + + public interface IdleObserver { + void onIdle(); + } } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 2ef10e720..cddd80132 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -47,7 +47,7 @@ import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.SystemUtils; -public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener { +public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver { private static final String TAG = TrackRecordingService.class.getSimpleName(); @@ -108,7 +108,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. recordingDataObservable = new MutableLiveData<>(NOT_RECORDING); trackPointCreator = new TrackPointCreator(this); - trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, handler); + trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, this , handler); voiceAnnouncementManager = new VoiceAnnouncementManager(this); notificationManager = new TrackRecordingServiceNotificationManager(this); @@ -296,11 +296,15 @@ public class TrackRecordingService extends Service implements TrackPointCreator. // Compute temporary track statistics using sensorData and update time. Pair> data = trackRecordingManager.getDataForUI(); - voiceAnnouncementManager.update(this, data.first); + voiceAnnouncementManager.announceStatisticsIfNeeded(data.first); recordingDataObservable.postValue(new RecordingData(data.first, data.second.first, data.second.second)); } + public void onIdle() { + voiceAnnouncementManager.announceIdle(); + } + @VisibleForTesting public void stopUpdateRecordingData() { handler.removeCallbacks(updateRecordingData); diff --git a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncement.java b/src/main/java/de/dennisguse/opentracks/services/announcement/TTSManager.java similarity index 73% rename from src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncement.java rename to src/main/java/de/dennisguse/opentracks/services/announcement/TTSManager.java index e8f10e7ad..2b3dc406d 100644 --- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncement.java +++ b/src/main/java/de/dennisguse/opentracks/services/announcement/TTSManager.java @@ -31,37 +31,19 @@ import java.util.List; import java.util.Locale; import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.data.ContentProviderUtils; -import de.dennisguse.opentracks.data.TrackPointIterator; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.Track; -import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.settings.PreferencesUtils; -import de.dennisguse.opentracks.stats.SensorStatistics; -import de.dennisguse.opentracks.stats.TrackStatistics; -import de.dennisguse.opentracks.ui.intervals.IntervalStatistics; -/** - * This class will announce the user's {@link TrackStatistics}. - * - * @author Sandor Dornbush - */ -public class VoiceAnnouncement { + +public class TTSManager { public final static int AUDIO_STREAM = TextToSpeech.Engine.DEFAULT_STREAM; - private static final String TAG = VoiceAnnouncement.class.getSimpleName(); + private static final String TAG = TTSManager.class.getSimpleName(); private final Context context; private final AudioManager audioManager; - private final ContentProviderUtils contentProviderUtils; - private TrackPoint.Id startTrackPointId = null; - - private IntervalStatistics intervalStatistics; - private Distance intervalDistance; - private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { @@ -108,12 +90,9 @@ public class VoiceAnnouncement { private MediaPlayer ttsFallback; - VoiceAnnouncement(Context context) { + TTSManager(Context context) { this.context = context; audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - contentProviderUtils = new ContentProviderUtils(context); - intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance(); - intervalStatistics = new IntervalStatistics(intervalDistance); } public void start() { @@ -139,7 +118,7 @@ public class VoiceAnnouncement { } } - public void announce(@NonNull Track track) { + public void announce(@NonNull Spannable announcement) { synchronized (this) { if (!ttsReady) { ttsReady = ttsInitStatus == TextToSpeech.SUCCESS; @@ -166,23 +145,6 @@ public class VoiceAnnouncement { return; } - Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance(); - if (currentIntervalDistance != intervalDistance) { - intervalStatistics = new IntervalStatistics(currentIntervalDistance); - intervalDistance = currentIntervalDistance; - startTrackPointId = null; - } - - TrackPointIterator trackPointIterator = new TrackPointIterator(contentProviderUtils, track.getId(), startTrackPointId); - startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator); - IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval(); - SensorStatistics sensorStatistics = null; - if (track.getId() != null) { - sensorStatistics = contentProviderUtils.getSensorStats(track.getId()); - } - - Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics); - if (announcement.length() > 0) { // 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"); diff --git a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java index 0bfe185af..7b5a4d5b0 100644 --- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java @@ -17,6 +17,7 @@ package de.dennisguse.opentracks.services.announcement; import android.content.Context; import android.content.SharedPreferences; +import android.text.Spannable; import android.util.Log; import androidx.annotation.NonNull; @@ -27,11 +28,15 @@ import androidx.mediarouter.media.MediaRouter; import java.time.Duration; import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.ContentProviderUtils; +import de.dennisguse.opentracks.data.TrackPointIterator; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Track; -import de.dennisguse.opentracks.services.TrackRecordingService; +import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.settings.PreferencesUtils; +import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.TrackStatistics; +import de.dennisguse.opentracks.ui.intervals.IntervalStatistics; /** * Execute a periodic task on a time or distance schedule. @@ -42,9 +47,9 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe private static final String TAG = VoiceAnnouncementManager.class.getSimpleName(); - private final TrackRecordingService trackRecordingService; + private final Context context; - private VoiceAnnouncement voiceAnnouncement; + private TTSManager voiceAnnouncement; private TrackStatistics trackStatistics; @@ -58,12 +63,22 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe @NonNull private Duration nextTotalTime = TOTALTIME_OFF; - public VoiceAnnouncementManager(@NonNull TrackRecordingService trackRecordingService) { - this.trackRecordingService = trackRecordingService; + + private final ContentProviderUtils contentProviderUtils; + private TrackPoint.Id startTrackPointId = null; + + private IntervalStatistics intervalStatistics; + private Distance intervalDistance; + + public VoiceAnnouncementManager(@NonNull Context context) { + this.context = context; + contentProviderUtils = new ContentProviderUtils(context); + intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance(); + intervalStatistics = new IntervalStatistics(intervalDistance); } public void start(@Nullable TrackStatistics trackStatistics) { - voiceAnnouncement = new VoiceAnnouncement(trackRecordingService); + voiceAnnouncement = new TTSManager(context); voiceAnnouncement.start(); update(trackStatistics); } @@ -74,10 +89,10 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe updateNextTaskDistance(); } - public void update(@NonNull Context context, @NonNull Track track) { + private boolean shouldNotAnnounce() { if (voiceAnnouncement == null) { Log.e(TAG, "Cannot update when in status shutdown."); - return; + return true; } if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker() @@ -85,6 +100,26 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe .getSelectedRoute() .isDeviceSpeaker()) { Log.i(TAG, "No voice announcement on device speaker."); + return true; + } + + return false; + } + + public void announceIdle() { + if (shouldNotAnnounce()) { + return; + } + + if (!PreferencesUtils.shouldVoiceAnnouncementIdle()) { + return; + } + + voiceAnnouncement.announce(VoiceAnnouncementUtils.createIdle(context)); + } + + public void announceStatisticsIfNeeded(@NonNull Track track) { + if (shouldNotAnnounce()) { return; } @@ -100,10 +135,29 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe } if (announce) { - voiceAnnouncement.announce(track); + voiceAnnouncement.announce(createAnnouncement(track)); } } + private Spannable createAnnouncement(Track track) { + Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance(); + if (currentIntervalDistance != intervalDistance) { + intervalStatistics = new IntervalStatistics(currentIntervalDistance); + intervalDistance = currentIntervalDistance; + startTrackPointId = null; + } + + TrackPointIterator trackPointIterator = new TrackPointIterator(contentProviderUtils, track.getId(), startTrackPointId); + startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator); + IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval(); + SensorStatistics sensorStatistics = null; + if (track.getId() != null) { + sensorStatistics = contentProviderUtils.getSensorStats(track.getId()); + } + + return VoiceAnnouncementUtils.createStatistics(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics); + } + public void stop() { if (voiceAnnouncement != null) { voiceAnnouncement.stop(); diff --git a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java index c06ec5db8..801de5b2b 100644 --- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java +++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java @@ -33,7 +33,12 @@ class VoiceAnnouncementUtils { private VoiceAnnouncementUtils() { } - static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) { + static Spannable createIdle(Context context) { + return new SpannableStringBuilder() + .append(context.getString(R.string.voiceIdle)); + } + + static Spannable createStatistics(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) { SpannableStringBuilder builder = new SpannableStringBuilder(); Distance totalDistance = trackStatistics.getTotalDistance(); Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed(); diff --git a/src/main/java/de/dennisguse/opentracks/settings/AnnouncementsSettingsFragment.java b/src/main/java/de/dennisguse/opentracks/settings/AnnouncementsSettingsFragment.java index a27e036a8..1bd67eb4a 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/AnnouncementsSettingsFragment.java +++ b/src/main/java/de/dennisguse/opentracks/settings/AnnouncementsSettingsFragment.java @@ -17,7 +17,7 @@ public class AnnouncementsSettingsFragment extends PreferenceFragmentCompat { @Override public void onStart() { super.onStart(); - ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_title); + ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_statistics_title); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 5a1af8bc9..9c40c4805 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -312,6 +312,10 @@ public class PreferencesUtils { return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT); } + public static boolean shouldVoiceAnnouncementIdle() { + return getBoolean(R.string.voice_announce_idle_key, true); + } + public static Duration getVoiceAnnouncementFrequency() { final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default)); int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT); diff --git a/src/main/res/values-b+es+419/strings.xml b/src/main/res/values-b+es+419/strings.xml index 9d16d1ac2..93b03420c 100644 --- a/src/main/res/values-b+es+419/strings.xml +++ b/src/main/res/values-b+es+419/strings.xml @@ -425,7 +425,7 @@ Filtrar Seleccionar diseño Importar/Exportar - Avisos de voz + Avisos de voz Tiempo, Distancia, Velocidad de la voz Importar, Exportar, Autoexportar Unidades diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 00f2a9a34..45afc9491 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -488,7 +488,7 @@ limitations under the License. Import, export, automatický export Import/Export Čas, vzdálenost, rychlost hlasu - Hlasová oznámení + Hlasová oznámení Bluetooth senzory Časový interval, Interval vzdálenosti, Maximální vzdálenost, Přesnost Vzhled, Rozložení záznamu, Téma diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml index aefa73f9d..dcea99d81 100644 --- a/src/main/res/values-da/strings.xml +++ b/src/main/res/values-da/strings.xml @@ -458,7 +458,7 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj Foretrukne enheder, standardaktivitet, spor standardnavn Tidsinterval, Distanceinterval, Maksimal afstand, Nøjagtighed Cykelsensorer, Hjulomkreds, Løbesensorer - Stemmemeddelelser + Stemmemeddelelser Tid, afstand, stemmehastighed Import/eksport Import, eksport, automatisk eksport diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index f05d0d4f6..aa6750f36 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -500,7 +500,7 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka Import/Export Bluetooth Sensoren Fahrradsensoren, Radumfang und Lauf-Sensoren - Sprachansagen + Sprachansagen Rückgängig Heute Gestern diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 5a717228f..8f2cccc7b 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -525,7 +525,7 @@ limitations under the License. Interfaz de usuario Apariencia, Diseño de grabación, Tema GPS - Avisos de voz + Avisos de voz Unidades Importar/Exportar Diseño predeterminado diff --git a/src/main/res/values-et/strings.xml b/src/main/res/values-et/strings.xml index b85dd0d9f..b65720a53 100644 --- a/src/main/res/values-et/strings.xml +++ b/src/main/res/values-et/strings.xml @@ -468,7 +468,7 @@ limitations under the License. Välimus, Salvestamise Paigutus, Teema Ajavahemik, vahemaa intervall, maksimaalne kaugus, täpsus Bluetooth Andurid - Häälteated + Häälteated Aeg, Kaugus, Hääle Kiirus Ühikud Import, Eksport, Automaatne Eksport diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index b41b5efde..80da5df5f 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -477,7 +477,7 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O %1$.1f mi/h (gomendatua) Bluetooth Denbora-tartea, Distantzia-tartea, Gehienezko Distantzia, Zehaztasuna - Ahots Iragarkiak + Ahots Iragarkiak Denbora, Distantzia, Ahotsaren Abiadura Inportatu/Esportatu Inportatu, Esportatu, Auto-Export diff --git a/src/main/res/values-fi/strings.xml b/src/main/res/values-fi/strings.xml index f7df29ec0..f4d6c853f 100644 --- a/src/main/res/values-fi/strings.xml +++ b/src/main/res/values-fi/strings.xml @@ -539,7 +539,7 @@ limitations under the License. Suositeltavat yksiköt, Oletusaktiviteetti, Seurannan oletusnimi Valitse\" Näytä kartalla\"käyttäytyminen Pyöräilyanturit, Pyörän ympärysmitta, Juoksuanturit - Ääni-ilmoitukset + Ääni-ilmoitukset Aika, etäisyys, äänennopeus Aikaväli, etäisyysväli, maksimietäisyys, tarkkuus \ No newline at end of file diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 54d27ff42..b32a67d6b 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -526,7 +526,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio Importez, Exportez, Auto Export Importez/Exportez Temps, distance, vitesse de la voix - Annonces vocales + Annonces vocales Capteurs Bluetooth Intervalle de temps, Intervalle de distance, Distance maximale, Précision GPS diff --git a/src/main/res/values-ga/strings.xml b/src/main/res/values-ga/strings.xml index 4a9fdbd67..216ec7958 100644 --- a/src/main/res/values-ga/strings.xml +++ b/src/main/res/values-ga/strings.xml @@ -352,7 +352,7 @@ Athshocraigh Socruithe go Luachanna Réamhshocraithe Eatramh ama Cuirfear gach socrú leagan amach ar ais chuig na luachanna réamhshocraithe. Ní scriosfaidh sé seo aon rianta ar an bhfeiste. - Fógraí Gutha + Fógraí Gutha Taispeáin staitisticí ar an scáileán glas Meánráta croí Athshocraigh do leagan amach\? diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 79d9da8af..55b9ea245 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -492,7 +492,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele Intervalo de tempo, Distancia intervalo, Max Distancia, Precisión Sensores Bluetooth Sensores de ciclismo, Circunferencia da roda, Sensores de carreira - Indicacións por Voz + Indicacións por Voz Importación/Exportación Importación, Exportación, Auto Exportación Unidades diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index be72cd72c..8305cc6a4 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -466,7 +466,7 @@ limitations under the License. Bluetooth Érzékelők Elbocsátás Kerékpáros Érzékelők, Kerék Kerülete, Futásérzékelők - Voice Közlemények + Voice Közlemények A fájlnév formátuma A rögzített adatokat nem osztják meg automatikusan más alkalmazásokkal. Eltávolítás diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 1096fb8d4..13a262685 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -489,7 +489,7 @@ Infatti, un\'applicazione deve supportare ACTION_VIEW con MIME applica GPS Intervallo di tempo, Intervallo di distanza, Massima Distanza, Precisione Sensori Bluetooth - Annunci Vocali + Annunci Vocali Tempo, Distanza, Velocità della Voce Importa/Esporta Importa, Esporta, Auto Esporta diff --git a/src/main/res/values-nb/strings.xml b/src/main/res/values-nb/strings.xml index cd2911194..99414bc1e 100644 --- a/src/main/res/values-nb/strings.xml +++ b/src/main/res/values-nb/strings.xml @@ -484,7 +484,7 @@ limitations under the License. GPS Tidsintervall, distanseintervall, maks, distanse, nøyaktighet Blåtannssensorer - Talemeldinger + Talemeldinger Tid, distanse, talehastighet Import/eksport Import, eksport, auto-eksport diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 63761ff6f..61c508830 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -532,7 +532,7 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid Tijdsinterval, afstandsinterval, maximale afstand, nauwkeurigheid Bluetooth sensoren Fiets sensoren, wielomtrek, loop sensoren - Gesproken berichten + Gesproken berichten Lay-outs Voer de naam van de lay-out in Lay-out naam bestaat al diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index c819c075c..35f54a329 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -474,7 +474,7 @@ limitations under the License. GPS Czujnik Bluetooth Czujniki rowerowe, Obwód koła, Czujniki biegowe - Wiadomości głosowe + Wiadomości głosowe Czas, Dystans, Głos prędkości Import/Eksport Jednostki diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 558c4a1f8..e151accf1 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -428,7 +428,7 @@ %1$.1f km/h %1$.1f km/h (Recomendado) Relógio - Anúncios de voz + Anúncios de voz Voice announcements Sensores Bluetooth Intervalo de tempo, Intervalo de distância, Distância máxima, Precisão diff --git a/src/main/res/values-pt-rPT/strings.xml b/src/main/res/values-pt-rPT/strings.xml index 61f51c279..07ae4326a 100644 --- a/src/main/res/values-pt-rPT/strings.xml +++ b/src/main/res/values-pt-rPT/strings.xml @@ -428,7 +428,7 @@ Intervalo de tempo, Intervalo de distância, Distância máxima, Precisão Sensores Bluetooth Voice announcements - Anúncios de voz + Anúncios de voz Tempo, distância, velocidade da voz Importar/Exportar Importar, Exportar, Auto Export diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index a0152c730..ba5ae80cb 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -460,7 +460,7 @@ limitations under the License. Voice announcements Aparência, Layout de gravação, Tema GPS - Anúncios de voz + Anúncios de voz Tempo, distância, velocidade da voz Importar/Exportar Importar, Exportar, Auto Export diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 5d255f783..aedc4d000 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -460,7 +460,7 @@ limitations under the License. Интервал времени и дистанции, Максимальная дистанция, Точность Bluetooth-датчики Датчики велосипеда, Оборот колеса, Датчики бега - Голосовые подсказки + Голосовые подсказки Импорт/Экспорт Импорт, Экспорт, Автоэкспорт Единицы diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index 8ca052929..94bb95760 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -483,7 +483,7 @@ limitations under the License. Predvolené jednotky, predvolená aktivita, predvolený názov skladby Používateľské rozhranie Vzhľad, Rozloženie nahrávania, Téma - Hlasové oznámenia + Hlasové oznámenia GPS Senzory Bluetooth Cyklistické snímače, obvod kolies, snímače behu diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index 79d357167..e0ff4f6e1 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -401,7 +401,7 @@ limitations under the License. s fotografijami Skladbe niso izvožene Ponastavitev postavitve po meri - Glasovna obvestila + Glasovna obvestila Čas, razdalja, hitrost glasu Uvoz/izvoz Privzete vrednosti dejavnosti diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 330d82566..27223c4d8 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -365,7 +365,7 @@ limitations under the License. GPS Tercih Edilen Birimler, Varsayılan Etkinlik, Varsayılan Parça Adı Zaman aralığı, Mesafe aralığı, Maks. Mesafe, Hassasiyet - Sesli Anonslar + Sesli Anonslar Zaman, Mesafe, Ses Hızı Sensör verileri, kaydedilen konum başına saklanır. Kalp atış hızı sensörü açısından bu, konum başına yalnızca bir kalp atış hızı ölçümünün depolandığı anlamına gelir. Hareketsiz duruyorsanız veya içerideyseniz, hiçbir sensör verisi kaydedilmeyecektir (ancak gösterilecektir). Yapabileceklerin: diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 8e3c1c8a4..0c98393d0 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -488,7 +488,7 @@ limitations under the License. Інтерфейс користувача Зовнішній вигляд, Макет запису, Тема Датчики Bluetooth - Голосові оголошення + Голосові оголошення Інтервал часу, інтервал відстані, Максимальна відстань, точність Датчики Їзди На Велосипеді, Окружність Колеса, Датчики Ходу Їзда на велосипеді diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index 0368129a2..8af5e35d8 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -487,7 +487,7 @@ limitations under the License. GPS Cảm Biến Bluetooth Đi Xe Đạp Cảm Biến, Bánh Vòng, Chạy Bộ Cảm Biến - Giọng Nói Thông Báo + Giọng Nói Thông Báo Thời Gian, Khoảng Cách, Tốc Độ Giọng Nói Xuất Nhập Khẩu Hoàn tác diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 6d4f84db4..fbe67df62 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -332,7 +332,7 @@ limitations under the License. 時間間隔、距離間隔、最大距離、精度 藍芽感測器 自行車感測器、輪周、跑步感測器 - 語音播報 + 語音播報 時間、距離、語速 匯入/匯出 匯入、匯出、自動匯出 diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index e89b5cc8b..42073fd0e 100644 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -558,7 +558,7 @@ limitations under the License. 默认的单位和活动类型 蓝牙传感器 自行车传感器,车轮周长,跑步传感器 - 语音播报 + 语音播报 将设置重置为默认值 重置自定义布局 配速(分钟/海里) diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index f35f4499e..9a7119366 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -237,12 +237,22 @@ voiceOnDeviceSpeaker true + voice_announce_idle_key + false + voiceAnnounceTotalDistance + true voiceAnnounceMovingTime + true voiceAnnounceAverageSpeedPace + true voiceAnnounceLapSpeedPace + true + voiceAnnounceAverageHeartRate + false voiceAnnounceLapHeartRate + false exportTrackFileFormat diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7cfc91654..d45d88455 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -356,7 +356,11 @@ limitations under the License. Bluetooth Sensors Cycling Sensors, Wheel Circumference, Running Sensors - Voice Announcements + + Idle Announcements + Idle + + Statistics Announcements Time, Distance, Voice Speed Import/Export @@ -558,6 +562,7 @@ limitations under the License. Off Smallest (recommended) %1$d s (recommended) + Becoming idle. {n, plural, =1 {1 hour} diff --git a/src/main/res/xml/settings.xml b/src/main/res/xml/settings.xml index 69cb5e7e7..8cbe9b1e0 100644 --- a/src/main/res/xml/settings.xml +++ b/src/main/res/xml/settings.xml @@ -31,7 +31,7 @@ android:icon="@drawable/ic_baseline_volume_up_24" android:key="@string/settings_announcements_key" android:summary="@string/settings_announcements_summary" - android:title="@string/settings_announcements_title" /> + android:title="@string/settings_announcements_statistics_title" /> + android:title="@string/settings_announcements_statistics_title"> - - - + - - - + + android:title="@string/settings_announcements_idle_title" /> + + + + + + + +