diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java new file mode 100644 index 000000000..e2e39eefe --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java @@ -0,0 +1,34 @@ +package de.dennisguse.opentracks.util; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import de.dennisguse.opentracks.stats.TripStatistics; + +@RunWith(AndroidJUnit4.class) +public class AnnouncementUtilsTest { + + private Context context = ApplicationProvider.getApplicationContext(); + + @Test + public void getAnnouncement() { + TripStatistics stats = new TripStatistics(); + stats.setTotalDistance(20000); + stats.setTotalTime(600000); + stats.setMovingTime(300000); + stats.setMaxSpeed(100); + stats.setTotalElevationGain(6000); + + // when + String announcement = AnnouncementUtils.getAnnouncement(context, stats); + + // then + Assert.assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement); + } +} \ 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/AnnouncementPeriodicTask.java index 2d1e49b14..a465f4ac5 100644 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java +++ b/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java @@ -25,12 +25,9 @@ import android.util.Log; import java.util.Locale; -import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.stats.TripStatistics; -import de.dennisguse.opentracks.util.PreferencesUtils; -import de.dennisguse.opentracks.util.StringUtils; -import de.dennisguse.opentracks.util.UnitConversions; +import de.dennisguse.opentracks.util.AnnouncementUtils; /** * This class will periodically announce the user's trip statistics. @@ -160,7 +157,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask { Log.i(TAG, "Speech is not allowed at this time."); return; } - speakAnnouncement(getAnnouncement(tripStatistics)); + String announcement = AnnouncementUtils.getAnnouncement(context, tripStatistics); + speakAnnouncement(announcement); } @Override @@ -169,8 +167,6 @@ public class AnnouncementPeriodicTask implements PeriodicTask { tts.shutdown(); tts = null; } - - } private void onTtsReady() { @@ -196,81 +192,4 @@ public class AnnouncementPeriodicTask implements PeriodicTask { // 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"); } - - private String getAnnouncement(TripStatistics tripStatistics) { - boolean metricUnits = PreferencesUtils.isMetricUnits(context); - boolean reportSpeed = PreferencesUtils.isReportSpeed(context); - double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM; - double distancePerTime = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH; - - if (distance == 0) { - return context.getString(R.string.voice_total_distance_zero); - } - - if (!metricUnits) { - distance *= UnitConversions.KM_TO_MI; - distancePerTime *= UnitConversions.KM_TO_MI; - } - - String rate; - if (reportSpeed) { - int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour; - rate = context.getResources().getQuantityString(speedId, getQuantityCount(distancePerTime), distancePerTime); - } else { - double timePerDistance = distancePerTime == 0 ? 0.0 : 1 / distancePerTime; - int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile; - long time = Math.round(timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS); - rate = context.getString(paceId, getAnnounceTime(time)); - } - - int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles; - String totalDistance = context.getResources().getQuantityString(totalDistanceId, getQuantityCount(distance), distance); - - return context.getString(R.string.voice_template, totalDistance, - getAnnounceTime(tripStatistics.getMovingTime()), rate); - } - - private String getAnnounceTime(long time) { - int[] parts = StringUtils.getTimeParts(time); - String seconds = context.getResources() - .getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]); - String minutes = context.getResources() - .getQuantityString(R.plurals.voiceMinutes, parts[1], parts[1]); - String hours = context.getResources() - .getQuantityString(R.plurals.voiceHours, parts[2], parts[2]); - StringBuilder sb = new StringBuilder(); - if (parts[2] != 0) { - sb.append(hours); - sb.append(" "); - } - sb.append(minutes); - sb.append(" "); - sb.append(seconds); - return sb.toString(); - } - - /** - * Gets the plural count to be used by getQuantityString. - * getQuantityString only supports integer quantities, not a double quantity like "2.2". - *
- * As a temporary workaround, we convert a double quantity to an integer quantity. - * If the double quantity is exactly 0, 1, or 2, then we can return these integer quantities. - * Otherwise, we cast the double quantity to an integer quantity. - * However, we need to make sure that if the casted value is 0, 1, or 2, we don't return those, instead, return the next biggest integer 3. - * - * @param d the double value - */ - private int getQuantityCount(double d) { - if (d == 0) { - return 0; - } else if (d == 1) { - return 1; - } else if (d == 2) { - return 2; - } else { - //TODO This seems weird; why not use Math.round(d) or Math.ceil()? - int count = (int) d; - return count < 3 ? 3 : count; - } - } } diff --git a/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java b/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java new file mode 100644 index 000000000..c46b8d60b --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java @@ -0,0 +1,88 @@ +package de.dennisguse.opentracks.util; + +import android.content.Context; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.stats.TripStatistics; + +public class AnnouncementUtils { + + private AnnouncementUtils() {} + + public static String getAnnouncement(Context context, TripStatistics tripStatistics) { + boolean metricUnits = PreferencesUtils.isMetricUnits(context); + boolean reportSpeed = PreferencesUtils.isReportSpeed(context); + double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM; + double distancePerTime = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH; + + if (distance == 0) { + return context.getString(R.string.voice_total_distance_zero); + } + + if (!metricUnits) { + distance *= UnitConversions.KM_TO_MI; + distancePerTime *= UnitConversions.KM_TO_MI; + } + + String rate; + if (reportSpeed) { + int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour; + rate = context.getResources().getQuantityString(speedId, getQuantityCount(distancePerTime), distancePerTime); + } else { + double timePerDistance = distancePerTime == 0 ? 0.0 : 1 / distancePerTime; + int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile; + long time = Math.round(timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS); + rate = context.getString(paceId, getAnnounceTime(context, time)); + } + + int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles; + String totalDistance = context.getResources().getQuantityString(totalDistanceId, getQuantityCount(distance), distance); + + return context.getString(R.string.voice_template, totalDistance, getAnnounceTime(context, tripStatistics.getMovingTime()), rate); + } + + public static String getAnnounceTime(Context context, long time) { + int[] parts = StringUtils.getTimeParts(time); + String seconds = context.getResources() + .getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]); + String minutes = context.getResources() + .getQuantityString(R.plurals.voiceMinutes, parts[1], parts[1]); + String hours = context.getResources() + .getQuantityString(R.plurals.voiceHours, parts[2], parts[2]); + StringBuilder sb = new StringBuilder(); + if (parts[2] != 0) { + sb.append(hours); + sb.append(" "); + } + sb.append(minutes); + sb.append(" "); + sb.append(seconds); + return sb.toString(); + } + + /** + * Gets the plural count to be used by getQuantityString. + * getQuantityString only supports integer quantities, not a double quantity like "2.2". + *
+ * As a temporary workaround, we convert a double quantity to an integer quantity. + * If the double quantity is exactly 0, 1, or 2, then we can return these integer quantities. + * Otherwise, we cast the double quantity to an integer quantity. + * However, we need to make sure that if the casted value is 0, 1, or 2, we don't return those, instead, return the next biggest integer 3. + * + * @param d the double value + */ + public static int getQuantityCount(double d) { + if (d == 0) { + return 0; + } else if (d == 1) { + return 1; + } else if (d == 2) { + return 2; + } else { + //TODO This seems weird; why not use Math.round(d) or Math.ceil()? + int count = (int) d; + return count < 3 ? 3 : count; + } + } +} +