mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-28 16:22:12 -07:00
Extended voice announcements (spoken updates)
- better scalability - setting for contents - inform about duration - inform about GPS status
This commit is contained in:
parent
f0684647c3
commit
3455702375
@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
@ -26,7 +27,6 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
@ -38,7 +38,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity android:name=".activity.VoiceAnnouncementsSettingsActivity"></activity>
|
||||
<activity
|
||||
android:name=".activity.ShowWorkoutMapActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.RingtonePreference;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public abstract class FitoTrackSettingsActivity extends PreferenceActivity {
|
||||
|
||||
protected void shareFile(Uri uri) {
|
||||
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
||||
intentShareFile.setDataAndType(uri, getContentResolver().getType(uri));
|
||||
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
startActivity(Intent.createChooser(intentShareFile, getString(R.string.shareFile)));
|
||||
|
||||
Log.d("Export", uri.toString());
|
||||
Log.d("Export", getContentResolver().getType(uri));
|
||||
try {
|
||||
Log.d("Export", new BufferedInputStream(getContentResolver().openInputStream(uri)).toString());
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void showErrorDialog(Exception e, @StringRes int title, @StringRes int message) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
||||
.setPositiveButton(R.string.okay, null)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
protected static final Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
|
||||
String stringValue = value.toString();
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
|
||||
} else if (preference instanceof RingtonePreference) {
|
||||
// For ringtone preferences, look up the correct display value
|
||||
// using RingtoneManager.
|
||||
if (TextUtils.isEmpty(stringValue)) {
|
||||
// Empty values correspond to 'silent' (no ringtone).
|
||||
preference.setSummary(R.string.pref_ringtone_silent);
|
||||
|
||||
} else {
|
||||
Ringtone ringtone = RingtoneManager.getRingtone(
|
||||
preference.getContext(), Uri.parse(stringValue));
|
||||
|
||||
if (ringtone == null) {
|
||||
// Clear the summary if there was a lookup error.
|
||||
preference.setSummary(null);
|
||||
} else {
|
||||
// Set the summary to reflect the new ringtone display
|
||||
// name.
|
||||
String name = ringtone.getTitle(preference.getContext());
|
||||
preference.setSummary(name);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
protected static void bindPreferenceSummaryToValue(Preference preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.getContext())
|
||||
.getString(preference.getKey(), ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
UnitUtils.setUnit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
|
||||
}
|
||||
@ -31,8 +31,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@ -52,11 +50,11 @@ import org.mapsforge.map.layer.overlay.Polyline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.UserPreferences;
|
||||
import de.tadris.fitness.announcement.AnnouncementGPSStatus;
|
||||
import de.tadris.fitness.announcement.VoiceAnnouncements;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.map.MapManager;
|
||||
import de.tadris.fitness.map.tilesource.TileSources;
|
||||
@ -66,7 +64,7 @@ import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.ThemeManager;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener {
|
||||
public class RecordWorkoutActivity extends FitoTrackActivity implements LocationListener.LocationChangeListener, WorkoutRecorder.WorkoutRecorderListener, VoiceAnnouncements.VoiceAnnouncementCallback {
|
||||
|
||||
public static String ACTIVITY= Workout.WORKOUT_TYPE_RUNNING;
|
||||
|
||||
@ -86,8 +84,8 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
private Intent locationListener;
|
||||
private Intent pressureService;
|
||||
private boolean saved= false;
|
||||
private TextToSpeech tts;
|
||||
private boolean ttsReady = false;
|
||||
|
||||
private VoiceAnnouncements voiceAnnouncements;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -108,10 +106,8 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
recorder= new WorkoutRecorder(this, ACTIVITY, this);
|
||||
recorder.start();
|
||||
|
||||
tts = new TextToSpeech(this, (int status) -> {
|
||||
ttsReady = status == TextToSpeech.SUCCESS && tts.setLanguage(Locale.getDefault())>=0;
|
||||
Log.d("Recorder", "TTS-ready: " + ttsReady);
|
||||
});
|
||||
voiceAnnouncements = new VoiceAnnouncements(this, this);
|
||||
|
||||
infoViews[0]= new InfoViewHolder(findViewById(R.id.recordInfo1Title), findViewById(R.id.recordInfo1Value));
|
||||
infoViews[1]= new InfoViewHolder(findViewById(R.id.recordInfo2Title), findViewById(R.id.recordInfo2Value));
|
||||
infoViews[2]= new InfoViewHolder(findViewById(R.id.recordInfo3Title), findViewById(R.id.recordInfo3Value));
|
||||
@ -187,40 +183,23 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
}).start();
|
||||
}
|
||||
|
||||
long lastSpokenUpdateTime = 0;
|
||||
int lastSpokenUpdateDistance = 0;
|
||||
|
||||
private void updateDescription() {
|
||||
long duration = recorder.getDuration();
|
||||
int distanceInMeters = recorder.getDistance();
|
||||
int distanceInMeters = recorder.getDistanceInMeters();
|
||||
final String distanceCaption = getString(R.string.workoutDistance);
|
||||
final String distance = UnitUtils.getDistance(distanceInMeters);
|
||||
final String avgSpeedCaption = getString(R.string.workoutAvgSpeed);
|
||||
final String avgSpeed = UnitUtils.getSpeed(Math.min(100d, recorder.getAvgSpeed()));
|
||||
if (isResumed) {
|
||||
timeView.setText(UnitUtils.getHourMinuteSecondTime(duration));
|
||||
infoViews[0].setText(distanceCaption, distance);
|
||||
infoViews[1].setText(getString(R.string.workoutBurnedEnergy), recorder.getCalories() + " kcal");
|
||||
infoViews[2].setText(avgSpeedCaption, avgSpeed);
|
||||
infoViews[2].setText(getString(R.string.workoutAvgSpeedShort), avgSpeed);
|
||||
infoViews[3].setText(getString(R.string.workoutPauseDuration), UnitUtils.getHourMinuteSecondTime(recorder.getPauseDuration()));
|
||||
}
|
||||
|
||||
final UserPreferences prefs = Instance.getInstance(this).userPreferences;
|
||||
final long intervalT = 60 * 1000 * prefs.getSpokenUpdateTimePeriod();
|
||||
final int intervalInMeters = (int) (1000.0 / UnitUtils.CHOSEN_SYSTEM.getDistanceFromKilometers(1) * prefs.getSpokenUpdateDistancePeriod());
|
||||
if (!ttsReady ||
|
||||
(intervalT == 0 || duration / intervalT == lastSpokenUpdateTime / intervalT)
|
||||
&& (intervalInMeters == 0 || distanceInMeters / intervalInMeters == lastSpokenUpdateDistance / intervalInMeters)
|
||||
) return;
|
||||
voiceAnnouncements.check(recorder);
|
||||
|
||||
final String text = distanceCaption + ": " + distance + ". "
|
||||
+ avgSpeedCaption + ": " + avgSpeed;
|
||||
|
||||
Log.d("Recorder", "TTS speak: " + text);
|
||||
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, "updateDescription" + duration);
|
||||
|
||||
lastSpokenUpdateTime = duration;
|
||||
lastSpokenUpdateDistance = distanceInMeters;
|
||||
}
|
||||
|
||||
private void stop(){
|
||||
@ -344,9 +323,8 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
mapView.destroyAll();
|
||||
AndroidGraphicFactory.clearResourceMemoryCache();
|
||||
|
||||
// Shutdown TTS engine
|
||||
ttsReady = false;
|
||||
tts.shutdown();
|
||||
// Shutdown TTS
|
||||
voiceAnnouncements.destroy();
|
||||
|
||||
super.onDestroy();
|
||||
if(wakeLock.isHeld()){
|
||||
@ -403,9 +381,22 @@ public class RecordWorkoutActivity extends FitoTrackActivity implements Location
|
||||
gpsFound= true;
|
||||
hideWaitOverlay();
|
||||
}
|
||||
|
||||
AnnouncementGPSStatus announcement = new AnnouncementGPSStatus(RecordWorkoutActivity.this);
|
||||
if (announcement.isEnabled()) {
|
||||
if (oldState == WorkoutRecorder.GpsState.SIGNAL_LOST) { // GPS Signal found
|
||||
voiceAnnouncements.speak(announcement.getSpokenGPSFound());
|
||||
} else if (state == WorkoutRecorder.GpsState.SIGNAL_LOST) {
|
||||
voiceAnnouncements.speak(announcement.getSpokenGPSLost());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVoiceAnnouncementIsReady(boolean available) {
|
||||
}
|
||||
|
||||
static class InfoViewHolder {
|
||||
final TextView titleView;
|
||||
final TextView valueView;
|
||||
|
||||
@ -25,127 +25,28 @@ import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.RingtonePreference;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.NumberPicker;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.announcement.VoiceAnnouncements;
|
||||
import de.tadris.fitness.export.BackupController;
|
||||
import de.tadris.fitness.export.RestoreController;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
|
||||
private void shareFile(Uri uri) {
|
||||
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
|
||||
intentShareFile.setDataAndType(uri, getContentResolver().getType(uri));
|
||||
intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
startActivity(Intent.createChooser(intentShareFile, getString(R.string.shareFile)));
|
||||
|
||||
Log.d("Export", uri.toString());
|
||||
Log.d("Export", getContentResolver().getType(uri));
|
||||
try {
|
||||
Log.d("Export", new BufferedInputStream(getContentResolver().openInputStream(uri)).toString());
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void showErrorDialog(Exception e, @StringRes int title, @StringRes int message){
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setMessage(getString(message) + "\n\n" + e.getMessage())
|
||||
.setPositiveButton(R.string.okay, null)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
private static final Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
|
||||
String stringValue = value.toString();
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
|
||||
} else if (preference instanceof RingtonePreference) {
|
||||
// For ringtone preferences, look up the correct display value
|
||||
// using RingtoneManager.
|
||||
if (TextUtils.isEmpty(stringValue)) {
|
||||
// Empty values correspond to 'silent' (no ringtone).
|
||||
preference.setSummary(R.string.pref_ringtone_silent);
|
||||
|
||||
} else {
|
||||
Ringtone ringtone = RingtoneManager.getRingtone(
|
||||
preference.getContext(), Uri.parse(stringValue));
|
||||
|
||||
if (ringtone == null) {
|
||||
// Clear the summary if there was a lookup error.
|
||||
preference.setSummary(null);
|
||||
} else {
|
||||
// Set the summary to reflect the new ringtone display
|
||||
// name.
|
||||
String name = ringtone.getTitle(preference.getContext());
|
||||
preference.setSummary(name);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.getContext())
|
||||
.getString(preference.getKey(), ""));
|
||||
}
|
||||
public class SettingsActivity extends FitoTrackSettingsActivity {
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
@ -166,7 +67,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
return true;
|
||||
});
|
||||
findPreference("speech").setOnPreferenceClickListener(preference -> {
|
||||
showSpeechConfig();
|
||||
checkTTSandShowConfig();
|
||||
return true;
|
||||
});
|
||||
findPreference("import").setOnPreferenceClickListener(preference -> {
|
||||
@ -180,6 +81,24 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
}
|
||||
|
||||
private VoiceAnnouncements voiceAnnouncements;
|
||||
|
||||
private void checkTTSandShowConfig() {
|
||||
voiceAnnouncements = new VoiceAnnouncements(this, available -> {
|
||||
if (available) {
|
||||
showSpeechConfig();
|
||||
} else {
|
||||
// TextToSpeech is not available
|
||||
Toast.makeText(SettingsActivity.this, R.string.ttsNotAvailable, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
voiceAnnouncements.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
private void showSpeechConfig() {
|
||||
startActivity(new Intent(this, VoiceAnnouncementsSettingsActivity.class));
|
||||
}
|
||||
|
||||
private void showExportDialog() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.exportData)
|
||||
@ -305,44 +224,6 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
d.create().show();
|
||||
}
|
||||
|
||||
private void showSpeechConfig() {
|
||||
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
||||
|
||||
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
||||
final SharedPreferences preferences= PreferenceManager.getDefaultSharedPreferences(this);
|
||||
d.setTitle(getString(R.string.pref_spoken_updates_summary));
|
||||
View v= getLayoutInflater().inflate(R.layout.dialog_spoken_updates_picker, null);
|
||||
|
||||
NumberPicker npT = v.findViewById(R.id.spokenUpdatesTimePicker);
|
||||
npT.setMaxValue(60);
|
||||
npT.setMinValue(0);
|
||||
npT.setFormatter(value -> value == 0 ? "No speech" : value + " min");
|
||||
final String updateTimeVariable = "spokenUpdateTimePeriod";
|
||||
npT.setValue(preferences.getInt(updateTimeVariable, 0));
|
||||
npT.setWrapSelectorWheel(false);
|
||||
|
||||
final String distanceUnit = " " + UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||
NumberPicker npD = v.findViewById(R.id.spokenUpdatesDistancePicker);
|
||||
npD.setMaxValue(10);
|
||||
npD.setMinValue(0);
|
||||
npD.setFormatter(value -> value == 0 ? "No speech" : value + distanceUnit);
|
||||
final String updateDistanceVariable = "spokenUpdateDistancePeriod";
|
||||
npD.setValue(preferences.getInt(updateDistanceVariable, 0));
|
||||
npD.setWrapSelectorWheel(false);
|
||||
|
||||
d.setView(v);
|
||||
|
||||
d.setNegativeButton(R.string.cancel, null);
|
||||
d.setPositiveButton(R.string.okay, (dialog, which) -> {
|
||||
preferences.edit()
|
||||
.putInt(updateTimeVariable, npT.getValue())
|
||||
.putInt(updateDistanceVariable, npD.getValue())
|
||||
.apply();
|
||||
});
|
||||
|
||||
d.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the {@link android.app.ActionBar}, if the API is available.
|
||||
*/
|
||||
@ -354,19 +235,4 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
UnitUtils.setUnit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
|
||||
addTitle(getString(R.string.workoutSpeed));
|
||||
|
||||
addKeyValue(getString(R.string.workoutAvgSpeed), UnitUtils.getSpeed(workout.avgSpeed),
|
||||
addKeyValue(getString(R.string.workoutAvgSpeedShort), UnitUtils.getSpeed(workout.avgSpeed),
|
||||
getString(R.string.workoutTopSpeed), UnitUtils.getSpeed(workout.topSpeed));
|
||||
|
||||
addSpeedDiagram();
|
||||
|
||||
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class VoiceAnnouncementsSettingsActivity extends FitoTrackSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setupActionBar();
|
||||
|
||||
setTitle(R.string.voiceAnnouncementsTitle);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences_voice_announcements);
|
||||
|
||||
findPreference("speechConfig").setOnPreferenceClickListener(preference -> {
|
||||
showSpeechConfig();
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void showSpeechConfig() {
|
||||
UnitUtils.setUnit(this); // Maybe the user changed unit system
|
||||
|
||||
final AlertDialog.Builder d = new AlertDialog.Builder(this);
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
d.setTitle(getString(R.string.pref_voice_announcements_summary));
|
||||
View v = getLayoutInflater().inflate(R.layout.dialog_spoken_updates_picker, null);
|
||||
|
||||
NumberPicker npT = v.findViewById(R.id.spokenUpdatesTimePicker);
|
||||
npT.setMaxValue(60);
|
||||
npT.setMinValue(0);
|
||||
npT.setFormatter(value -> value == 0 ? "No speech" : value + " min");
|
||||
final String updateTimeVariable = "spokenUpdateTimePeriod";
|
||||
npT.setValue(preferences.getInt(updateTimeVariable, 0));
|
||||
npT.setWrapSelectorWheel(false);
|
||||
|
||||
final String distanceUnit = " " + UnitUtils.CHOSEN_SYSTEM.getLongDistanceUnit();
|
||||
NumberPicker npD = v.findViewById(R.id.spokenUpdatesDistancePicker);
|
||||
npD.setMaxValue(10);
|
||||
npD.setMinValue(0);
|
||||
npD.setFormatter(value -> value == 0 ? "No speech" : value + distanceUnit);
|
||||
final String updateDistanceVariable = "spokenUpdateDistancePeriod";
|
||||
npD.setValue(preferences.getInt(updateDistanceVariable, 0));
|
||||
npD.setWrapSelectorWheel(false);
|
||||
|
||||
d.setView(v);
|
||||
|
||||
d.setNegativeButton(R.string.cancel, null);
|
||||
d.setPositiveButton(R.string.okay, (dialog, which) ->
|
||||
preferences.edit()
|
||||
.putInt(updateTimeVariable, npT.getValue())
|
||||
.putInt(updateDistanceVariable, npD.getValue())
|
||||
.apply());
|
||||
|
||||
d.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the {@link android.app.ActionBar}, if the API is available.
|
||||
*/
|
||||
private void setupActionBar() {
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (actionBar != null) {
|
||||
// Show the Up button in the action bar.
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
|
||||
public abstract class Announcement {
|
||||
|
||||
private Context context;
|
||||
|
||||
Announcement(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("announcement_" + getId(), isEnabledByDefault());
|
||||
}
|
||||
|
||||
protected String getString(@StringRes int resId) {
|
||||
return context.getString(resId);
|
||||
}
|
||||
|
||||
public abstract String getId();
|
||||
|
||||
abstract boolean isEnabledByDefault();
|
||||
|
||||
abstract String getSpoken(WorkoutRecorder recorder);
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class AnnouncementAverageSpeed extends Announcement {
|
||||
|
||||
public AnnouncementAverageSpeed(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "avgSpeed";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSpoken(WorkoutRecorder recorder) {
|
||||
String avgSpeed = UnitUtils.getSpeed(recorder.getAvgSpeed());
|
||||
return getString(R.string.workoutAvgSpeedLong) + ": " + avgSpeed + ".";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class AnnouncementDistance extends Announcement {
|
||||
|
||||
public AnnouncementDistance(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "distance";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSpoken(WorkoutRecorder recorder) {
|
||||
final String distance = UnitUtils.getDistance(recorder.getDistanceInMeters());
|
||||
return getString(R.string.workoutDistance) + ": " + distance + ".";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
|
||||
public class AnnouncementDuration extends Announcement {
|
||||
|
||||
public AnnouncementDuration(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "duration";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSpoken(WorkoutRecorder recorder) {
|
||||
return getString(R.string.workoutDuration) + ": " + getSpokenTime(recorder.getDuration()) + ".";
|
||||
}
|
||||
|
||||
private String getSpokenTime(long duration) {
|
||||
final long minute = 1000L * 60;
|
||||
final long hour = minute * 60;
|
||||
|
||||
StringBuilder spokenTime = new StringBuilder();
|
||||
|
||||
if (duration > hour) {
|
||||
long hours = duration / hour;
|
||||
duration = duration % hour; // Set duration to the rest
|
||||
spokenTime.append(hours).append(" ");
|
||||
spokenTime.append(getString(hours == 1 ? R.string.timeHourSingular : R.string.timeHourPlural)).append(" ")
|
||||
.append(getString(R.string.and)).append(" ");
|
||||
}
|
||||
long minutes = duration / minute;
|
||||
spokenTime.append(minutes).append(" ");
|
||||
spokenTime.append(getString(minutes == 1 ? R.string.timeMinuteSingular : R.string.timeMinutePlural));
|
||||
|
||||
return spokenTime.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
|
||||
public class AnnouncementGPSStatus extends Announcement {
|
||||
|
||||
public AnnouncementGPSStatus(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "gps-lost";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSpoken(WorkoutRecorder recorder) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getSpokenGPSLost() {
|
||||
return getString(R.string.gpsLost);
|
||||
}
|
||||
|
||||
public String getSpokenGPSFound() {
|
||||
return getString(R.string.gpsFound);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AnnouncementManager {
|
||||
|
||||
private Context context;
|
||||
private List<Announcement> announcements = new ArrayList<>();
|
||||
|
||||
public AnnouncementManager(Context context) {
|
||||
this.context = context;
|
||||
addAnnouncements();
|
||||
}
|
||||
|
||||
private void addAnnouncements() {
|
||||
announcements.add(new AnnouncementGPSStatus(context));
|
||||
announcements.add(new AnnouncementDuration(context));
|
||||
announcements.add(new AnnouncementDistance(context));
|
||||
announcements.add(new AnnouncementAverageSpeed(context));
|
||||
}
|
||||
|
||||
public List<Announcement> getAnnouncements() {
|
||||
return announcements;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
*
|
||||
* This file is part of FitoTrack
|
||||
*
|
||||
* FitoTrack is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* FitoTrack is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.tadris.fitness.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.data.UserPreferences;
|
||||
import de.tadris.fitness.recording.WorkoutRecorder;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
|
||||
public class VoiceAnnouncements {
|
||||
|
||||
private TextToSpeech textToSpeech;
|
||||
private boolean ttsAvailable;
|
||||
private VoiceAnnouncementCallback callback;
|
||||
private final AnnouncementManager manager;
|
||||
|
||||
private long lastSpokenUpdateTime = 0;
|
||||
private int lastSpokenUpdateDistance = 0;
|
||||
|
||||
private final long intervalTime;
|
||||
private final int intervalInMeters;
|
||||
|
||||
public VoiceAnnouncements(Context context, VoiceAnnouncementCallback callback) {
|
||||
this.callback = callback;
|
||||
UserPreferences prefs = Instance.getInstance(context).userPreferences;
|
||||
textToSpeech = new TextToSpeech(context, this::ttsReady);
|
||||
|
||||
this.intervalTime = 60 * 1000 * prefs.getSpokenUpdateTimePeriod();
|
||||
this.intervalInMeters = (int) (1000.0 / UnitUtils.CHOSEN_SYSTEM.getDistanceFromKilometers(1) * prefs.getSpokenUpdateDistancePeriod());
|
||||
|
||||
this.manager = new AnnouncementManager(context);
|
||||
}
|
||||
|
||||
private void ttsReady(int status) {
|
||||
ttsAvailable = status == TextToSpeech.SUCCESS && textToSpeech.setLanguage(Locale.getDefault()) >= 0;
|
||||
callback.onVoiceAnnouncementIsReady(ttsAvailable);
|
||||
}
|
||||
|
||||
public void check(WorkoutRecorder recorder) {
|
||||
if (!ttsAvailable) {
|
||||
return;
|
||||
} // Cannot speak
|
||||
|
||||
boolean shouldSpeak = false;
|
||||
|
||||
if (intervalTime != 0 && recorder.getDuration() - lastSpokenUpdateTime > intervalTime) {
|
||||
shouldSpeak = true;
|
||||
}
|
||||
if (intervalInMeters != 0 && recorder.getDistanceInMeters() - lastSpokenUpdateDistance > intervalInMeters) {
|
||||
shouldSpeak = true;
|
||||
}
|
||||
|
||||
if (shouldSpeak) {
|
||||
speak(recorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void speak(WorkoutRecorder recorder) {
|
||||
for (Announcement announcement : manager.getAnnouncements()) {
|
||||
speak(recorder, announcement);
|
||||
}
|
||||
|
||||
lastSpokenUpdateTime = recorder.getDuration();
|
||||
lastSpokenUpdateDistance = recorder.getDistanceInMeters();
|
||||
}
|
||||
|
||||
private void speak(WorkoutRecorder recorder, Announcement announcement) {
|
||||
if (!announcement.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
String text = announcement.getSpoken(recorder);
|
||||
if (!text.equals("")) {
|
||||
speak(text);
|
||||
}
|
||||
}
|
||||
|
||||
private int speakId = 1;
|
||||
|
||||
public void speak(String text) {
|
||||
if (!ttsAvailable) {
|
||||
return;
|
||||
} // Cannot speak
|
||||
Log.d("Recorder", "TTS speaks: " + text);
|
||||
textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, null, "announcement" + (++speakId));
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
textToSpeech.shutdown();
|
||||
}
|
||||
|
||||
public interface VoiceAnnouncementCallback {
|
||||
void onVoiceAnnouncementIsReady(boolean available);
|
||||
}
|
||||
}
|
||||
@ -36,11 +36,11 @@ public class UserPreferences {
|
||||
}
|
||||
|
||||
public int getSpokenUpdateTimePeriod(){
|
||||
return preferences.getInt("spokenUpdateTimePeriod", 5);
|
||||
return preferences.getInt("spokenUpdateTimePeriod", 0);
|
||||
}
|
||||
|
||||
public int getSpokenUpdateDistancePeriod(){
|
||||
return preferences.getInt("spokenUpdateDistancePeriod", 1);
|
||||
return preferences.getInt("spokenUpdateDistancePeriod", 0);
|
||||
}
|
||||
|
||||
public String getMapStyle(){
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
android:id="@+id/recordInfo3Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/workoutAvgSpeed"
|
||||
android:text="@string/workoutAvgSpeedShort"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="true"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -51,10 +51,6 @@
|
||||
<string name="pref_weight">Dein Gewicht</string>
|
||||
<string name="pref_weight_summary">Dein Gewicht ist zur Kalorienberechnung wichtig</string>
|
||||
|
||||
<!-- please translate -->
|
||||
<string name="pref_spoken_updates">Spoken Updates</string>
|
||||
<string name="pref_spoken_updates_summary">Choose the time and distance between spoken updates</string>
|
||||
|
||||
<string name="preferences">Einstellungen</string>
|
||||
<string name="recordWorkout">Workout aufzeichnen</string>
|
||||
<string name="restore">Wiederherstellen</string>
|
||||
@ -69,7 +65,7 @@
|
||||
<string name="trackerRunningMessage">Dein Workout wird aufgezeichnet</string>
|
||||
<string name="trackingInfo">Tracking Info</string>
|
||||
<string name="trackingInfoDescription">Info über den laufenden Tracker</string>
|
||||
<string name="workoutAvgSpeed">Durchschnittsgeschw.</string>
|
||||
<string name="workoutAvgSpeedShort">Durchschnittsgeschw.</string>
|
||||
<string name="workoutBurnedEnergy">Verbrannte Kalorien</string>
|
||||
<string name="workoutDate">Datum</string>
|
||||
<string name="workoutDistance">Distanz</string>
|
||||
@ -112,4 +108,19 @@
|
||||
<string name="uploadFailed">Upload fehlgeschlagen</string>
|
||||
<string name="uploadSuccessful">Upload erfolgreich</string>
|
||||
<string name="uploadFailedOsmNotAuthorized">Nicht autorisiert, nocheinmal versuchen</string>
|
||||
<string name="ttsNotAvailable">Sprachausgabe ist nicht verfügbar</string>
|
||||
<string name="workoutAvgSpeedLong">Durchschnittsgeschwindigkeit</string>
|
||||
<string name="and">und</string>
|
||||
<string name="announcementGPSStatus">GPS Status</string>
|
||||
<string name="gpsFound">GPS Signal gefunden</string>
|
||||
<string name="gpsLost">GPS Signal verloren</string>
|
||||
<string name="pref_announcements_config_summary">Wähle die Zeit und Distanz zwischen den Sprachansagen</string>
|
||||
<string name="pref_announcements_config_title">Auslöser der Ansage</string>
|
||||
<string name="pref_announcements_content">Inhalt der Ansagen</string>
|
||||
<string name="pref_voice_announcements_summary">Konfiguriere Sprachansagen, die dich während des Trainings informieren</string>
|
||||
<string name="timeHourPlural">Stunden</string>
|
||||
<string name="timeHourSingular">Stunde</string>
|
||||
<string name="timeMinutePlural">Minuten</string>
|
||||
<string name="timeMinuteSingular">Minute</string>
|
||||
<string name="voiceAnnouncementsTitle">Sprachansagen</string>
|
||||
</resources>
|
||||
|
||||
@ -47,6 +47,26 @@
|
||||
<string name="setPreferencesTitle">Set Preferences</string>
|
||||
<string name="setPreferencesMessage">You can set your preferred unit system and your weight in the settings.</string>
|
||||
|
||||
<string name="timeMinuteSingular">Minute</string>
|
||||
<string name="timeMinutePlural">Minutes</string>
|
||||
<string name="timeHourSingular">Hour</string>
|
||||
<string name="timeHourPlural">Hours</string>
|
||||
<string name="and">and</string>
|
||||
|
||||
<string name="announcementGPSStatus">GPS Status</string>
|
||||
|
||||
<string name="voiceAnnouncementsTitle">Voice Announcements</string>
|
||||
|
||||
<string name="pref_voice_announcements_summary">Configure voice prompts that inform you during the workout</string>
|
||||
|
||||
<string name="pref_announcements_config_title">Announcement Trigger</string>
|
||||
<string name="pref_announcements_config_summary">Choose the time and distance between voice announcements</string>
|
||||
|
||||
<string name="pref_announcements_content">Content of the announcements</string>
|
||||
|
||||
<string name="gpsLost">GPS signal lost</string>
|
||||
<string name="gpsFound">GPS signal found</string>
|
||||
|
||||
<string name="workoutTime">Time</string>
|
||||
<string name="workoutDate">Date</string>
|
||||
<string name="workoutDuration">Duration</string>
|
||||
@ -57,7 +77,8 @@
|
||||
<string name="workoutPace">Pace</string>
|
||||
<string name="workoutRoute">Route</string>
|
||||
<string name="workoutSpeed">Speed</string>
|
||||
<string name="workoutAvgSpeed">Average Speed</string>
|
||||
<string name="workoutAvgSpeedShort">Avg. Speed</string>
|
||||
<string name="workoutAvgSpeedLong">Average Speed</string>
|
||||
<string name="workoutTopSpeed">Top Speed</string>
|
||||
<string name="workoutBurnedEnergy">Burned Energy</string>
|
||||
<string name="workoutTotalEnergy">Total Energy</string>
|
||||
@ -112,8 +133,6 @@
|
||||
<string name="exportAsGpxFile">Export as GPX-File</string>
|
||||
<string name="pref_weight">Your Weight</string>
|
||||
<string name="pref_weight_summary">Your weight is needed to calculate the burned calories</string>
|
||||
<string name="pref_spoken_updates">Spoken Updates</string>
|
||||
<string name="pref_spoken_updates_summary">Choose the time and distance between spoken updates</string>
|
||||
<string name="pref_unit_system">Preferred system of units</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="exportData">Export Data</string>
|
||||
@ -128,4 +147,5 @@
|
||||
<string name="cut">Cut the first/last 300 Meters</string>
|
||||
<string name="trackVisibilityPref">Track Visibility</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="ttsNotAvailable">TextToSpeech is not available</string>
|
||||
</resources>
|
||||
|
||||
@ -44,8 +44,8 @@
|
||||
android:key="speech"
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
android:summary="@string/pref_spoken_updates_summary"
|
||||
android:title="@string/pref_spoken_updates" />
|
||||
android:summary="@string/pref_voice_announcements_summary"
|
||||
android:title="@string/voiceAnnouncementsTitle" />
|
||||
|
||||
|
||||
<PreferenceCategory android:title="@string/data">
|
||||
|
||||
51
app/src/main/res/xml/preferences_voice_announcements.xml
Normal file
51
app/src/main/res/xml/preferences_voice_announcements.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Jannis Scheibe <jannis@tadris.de>
|
||||
~
|
||||
~ This file is part of FitoTrack
|
||||
~
|
||||
~ FitoTrack is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ FitoTrack is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
||||
<Preference
|
||||
android:key="speechConfig"
|
||||
android:title="@string/pref_announcements_config_title"
|
||||
android:summary="@string/pref_announcements_config_summary" />
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_announcements_content">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="announcement_gps-lost"
|
||||
android:title="@string/announcementGPSStatus" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="announcement_duration"
|
||||
android:title="@string/workoutDuration" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="announcement_distance"
|
||||
android:title="@string/workoutDistance" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="announcement_avgSpeed"
|
||||
android:title="@string/workoutAvgSpeedLong" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
Loading…
x
Reference in New Issue
Block a user