Skip to content

Commit

Permalink
Idle: add voice announcement (user configurable).
Browse files Browse the repository at this point in the history
Fixes of #1174
  • Loading branch information
dennisguse committed Sep 25, 2023
1 parent 9a50d7b commit 83da4f8
Show file tree
Hide file tree
Showing 40 changed files with 192 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void getAnnouncement_metric_speed() {
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);
Expand All @@ -79,7 +79,7 @@ public void getAnnouncement_metric_speed_rounding_check() {
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);
Expand All @@ -95,7 +95,7 @@ public void getAnnouncement_metric_distance_rounding_check() {
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);
Expand All @@ -111,7 +111,7 @@ public void getAnnouncement_metric_distance_rounding_check_two() {
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);
Expand All @@ -133,7 +133,7 @@ public void getAnnouncement_withInterval_metric_speed() {
}

// 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);
Expand All @@ -149,7 +149,7 @@ public void getAnnouncement_metric_pace() {
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);
Expand All @@ -171,7 +171,7 @@ public void getAnnouncement_withInterval_metric_pace() {
}

// 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);
Expand All @@ -187,7 +187,7 @@ public void getAnnouncement_imperial_speed() {
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);
Expand All @@ -201,7 +201,7 @@ public void getAnnouncement_imperial_speed_1() {
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);
Expand All @@ -215,7 +215,7 @@ public void getAnnouncement_imperial_meter_speed_1() {
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);
Expand All @@ -229,7 +229,7 @@ public void getAnnouncement_metric_speed_1() {
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);
Expand All @@ -251,7 +251,7 @@ public void getAnnouncement_withInterval_imperial_speed() {
}

// 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);
Expand All @@ -267,7 +267,7 @@ public void getAnnouncement_imperial_pace() {
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);
Expand All @@ -289,7 +289,7 @@ public void getAnnouncement_withInterval_imperial_pace() {
}

// 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);
Expand All @@ -315,7 +315,7 @@ public void getAnnouncement_heart_rate_and_sensor_statistics() {
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);
Expand Down Expand Up @@ -345,7 +345,7 @@ public void getAnnouncement_only_lap_heart_rate() {
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);
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/de/dennisguse/opentracks/AbstractActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import androidx.appcompat.app.AppCompatActivity;

import de.dennisguse.opentracks.services.announcement.VoiceAnnouncement;
import de.dennisguse.opentracks.services.announcement.TTSManager;

/**
* @author Jimmy Shih
Expand All @@ -33,7 +33,7 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Set volume control stream for text to speech
setVolumeControlStream(VoiceAnnouncement.AUDIO_STREAM);
setVolumeControlStream(TTSManager.AUDIO_STREAM);

setContentView(getRootView());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -165,6 +167,8 @@ public Marker.Id insertMarker(String name, String category, String description,
public void onIdle() {
Log.d(TAG, "Becoming idle");
onNewTrackPoint(trackPointCreator.createIdle());

idleObserver.onIdle();
}

/**
Expand Down Expand Up @@ -270,6 +274,7 @@ private void insertTrackPointHelper(@NonNull TrackPoint trackPoint) {
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).
Expand Down Expand Up @@ -299,4 +304,8 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
idleDuration = PreferencesUtils.getIdleDurationTimeout();
}
}

public interface IdleObserver {
void onIdle();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
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();

Expand Down Expand Up @@ -108,7 +108,7 @@ public void onCreate() {
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);
Expand Down Expand Up @@ -296,11 +296,15 @@ private void updateRecordingDataWhileRecording() {
// Compute temporary track statistics using sensorData and update time.
Pair<Track, Pair<TrackPoint, SensorDataSet>> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,19 @@
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) {
Expand Down Expand Up @@ -108,12 +90,9 @@ public void onError(String utteranceId) {

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() {
Expand All @@ -139,7 +118,7 @@ public void start() {
}
}

public void announce(@NonNull Track track) {
public void announce(@NonNull Spannable announcement) {
synchronized (this) {
if (!ttsReady) {
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
Expand All @@ -166,23 +145,6 @@ public void announce(@NonNull Track track) {
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");
Expand Down
Loading

0 comments on commit 83da4f8

Please sign in to comment.