diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 000000000..f8051a6f9
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 1761f192b..ad98f2481 100644
--- a/build.gradle
+++ b/build.gradle
@@ -145,4 +145,4 @@ dependencies {
androidTestImplementation 'org.mockito:mockito-android:5.8.0'
androidTestUtil 'androidx.test:orchestrator:1.4.2'
-}
+}
\ No newline at end of file
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index c0b67324d..80ab72a04 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -64,6 +64,8 @@ limitations under the License.
+
+
+
specifies default stream of audio
+ // Adding log messages related to the TTSManager class
private static final String TAG = TTSManager.class.getSimpleName();
+ //Super important : Context class provides access to system services & application resources
private final Context context;
+ //AudioManager Class : Manage audio focus on the device (audio playback & recording)
private final AudioManager audioManager;
+ //AudioManager.OnAudioFocusChangeListener : Interface in Android SDK
+ // Monitors audio focus change and returns a LOG of the change
+
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
+
@Override
- public void onAudioFocusChange(int focusChange) {
- Log.d(TAG, "Audio focus changed to " + focusChange);
+ public void onAudioFocusChange(int focusChange) { // Method implementation of interface AudioManager.OnAudioFocusChangeListener
+
+ Log.d(TAG, "Audio focus changed to " + focusChange); // Logging a message to the class
+
+ //Constants that can be returned by the AudioManager Class
+ // _LOSS : system lost audio focus and can no longer be used
+ // _LOSS_TRANSIENT : Temporary loss of audio focus
+ // ''_CAN_DUCK : Lowers the volume of the media player (if another app is playing for example)
boolean stop = List.of(AudioManager.AUDIOFOCUS_LOSS, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)
- .contains(focusChange);
+ .contains(focusChange); // checking the state of 'focusChange' parameter
if (stop && tts != null && tts.isSpeaking()) {
+
+ // Interrupts the current utterance (whether played or rendered to file) and discards other utterances in the queue.
+ // Returns ERROR or SUCCESS.
tts.stop();
Log.i(TAG, "Aborting current tts due to focus change " + focusChange);
}
}
};
-
+ // 'UtteranceProgressListener' : interface & defines methods that can be called to monitor the progress of TTS utterance
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
+ // These methods can be really good for BackEnd to UI implementation
@Override
- public void onStart(String utteranceId) {
+ public void onStart(String utteranceId) { //when utterance begins
int result = audioManager.requestAudioFocus(audioFocusChangeListener, AUDIO_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.w(TAG, "Failed to request audio focus.");
@@ -69,7 +100,7 @@ public void onStart(String utteranceId) {
}
@Override
- public void onDone(String utteranceId) {
+ public void onDone(String utteranceId) { // when utterance has been successfully spoken
int result = audioManager.abandonAudioFocus(audioFocusChangeListener);
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.w(TAG, "Failed to relinquish audio focus.");
@@ -77,35 +108,52 @@ public void onDone(String utteranceId) {
}
@Override
- public void onError(String utteranceId) {
+ public void onError(String utteranceId) { // Called if an error occurs during the utterance
Log.e(TAG, "An error occurred for utteranceId " + utteranceId);
}
};
- private TextToSpeech tts;
+ private TextToSpeech tts; //Conversion MY TTS OBJECT
// Response from TTS after its initialization
- private int ttsInitStatus = TextToSpeech.ERROR;
+ private int ttsInitStatus = TextToSpeech.ERROR; //Stores initialization status =>
+ // Can/will change to .SUCCESS or .FAILED_SYNTHESIS
+ // A boolean field that indicates whether the text-to-speech engine has been properly initialized
+// and is ready to perform text-to-speech conversion.
+// private boolean ttsReady = false;
private boolean ttsReady = false;
private MediaPlayer ttsFallback;
+// MediaPlayer class: used to play a pre-recorded audio file as a fallback mechanism when
+// the TTS engine is unable to perform the TTS conversion.
- TTSManager(Context context) {
+ TTSManager(Context context) { // Constructor of the TTSManager class
this.context = context;
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+// TTSManager constructor initializes audioManager by calling
+// getSystemService(Context.AUDIO_SERVICE) on the Context object, which returns an
+// AudioManager object.
}
- public void start() {
+ public void start() { // Called in the VoiceAnnouncementManager Class
+
+
Log.d(TAG, "Start");
if (tts == null) {
+
tts = new TextToSpeech(context, status -> {
Log.i(TAG, "TextToSpeech initialized with status " + status);
+
ttsInitStatus = status;
});
}
if (ttsFallback == null) {
ttsFallback = MediaPlayer.create(context, R.raw.tts_fallback);
+
+ // initialize a new instance of MediaPlayer & associates it with a sound file resource in the app's raw directory
+ //R.raw.tts_fallback : contains reference to the sound file
+
if (ttsFallback == null) {
Log.w(TAG, "MediaPlayer for ttsFallback could not be created.");
} else {
@@ -118,10 +166,14 @@ public void start() {
}
}
- public void announce(@NonNull Spannable announcement) {
- synchronized (this) {
- if (!ttsReady) {
+ public void announce(@NonNull Spannable announcement) { // parameter cannot be null
+ synchronized (this) { // One announcement at a time
+
+ if (!ttsReady) { // checking if ready to make an announcement
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
+ ;
+ Log.d(TAG, "TTS initialized successfully.");
+ // Update the UI to indicate that TTS is ready.
if (ttsReady) {
onTtsReady();
}
@@ -145,7 +197,7 @@ public void announce(@NonNull Spannable announcement) {
return;
}
- if (announcement.length() > 0) {
+ if (announcement.length() > 0) { //Where it is gonna speak and start announcement / IMPORTANT
// 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");
}
@@ -163,19 +215,91 @@ public void stop() {
}
}
- private void onTtsReady() {
- Locale locale = Locale.getDefault();
- int languageAvailability = tts.isLanguageAvailable(locale);
- if (languageAvailability == TextToSpeech.LANG_MISSING_DATA || languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
- Log.w(TAG, "Default locale not available, use English.");
- locale = Locale.ENGLISH;
- /*
- * TODO: instead of using english, load the language if missing and show a toast if not supported.
- * Not able to change the resource strings to English.
- */
+// void onTtsReady() {
+// Locale locale = Locale.getDefault(); // Get default geolocation / region etc
+// int languageAvailability = tts.isLanguageAvailable(locale); //Method in the TTS class :
+// // checks is the engine can speak specified language
+// if (languageAvailability == TextToSpeech.LANG_MISSING_DATA || languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
+// Log.w(TAG, "Default locale not available, use English.");
+// locale = Locale.ENGLISH;
+//
+//
+// }
+// tts.setLanguage(locale); //set method in android sdk
+// tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate()); // Set speech rate output based on app preferences (set by user)
+// // track the progress of the TTS utterance,and to perform certain actions
+// // when specific events in the lifecycle of an utterance occur.
+// tts.setOnUtteranceProgressListener(utteranceListener);
+//
+//
+// }
+
+ //method is where you initialize your activity, set up the user interface,
+ // and perform any other necessary setup tasks.
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Call onTtsReady for initialization
+ onTtsReady();
+ }
+
+ void onTtsReady() {
+ // setContentView(R.layout.track_stopped);
+ // ImageView finish = findViewById(R.id.finish_button);
+
+
+// Set the content view of the activity to the layout defined in 'track_recording.xml'
+ setContentView(R.layout.track_recording);
+ // Find the FloatingActionButton with the id 'track_recording_fab_action' in the layout
+ FloatingActionButton run = findViewById(R.id.track_recording_fab_action);
+
+ if (run != null) {
+ // Initialize TextToSpeech instance with the application context
+ tts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
+ @Override
+ public void onInit(int ttsInitStatus) {
+ // Get the default locale of the device
+ Locale locale = Locale.getDefault();
+ int languageAvailability = tts.isLanguageAvailable(locale);
+ if (languageAvailability == TextToSpeech.LANG_MISSING_DATA || languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
+ Log.w(TAG, "Default locale not available, use English.");
+ // Set TextToSpeech language to English
+ locale = Locale.ENGLISH;
+ tts.setLanguage(locale);
+ // Set speech rate according to user preferences
+ tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate());
+ tts.setOnUtteranceProgressListener(utteranceListener);
+
+ // Set OnClickListener for FloatingActionButton 'run'
+ run.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ //Triggers the speaking FloatingActionButton is clicked
+ String speech = "Hello User!";
+ tts.speak(speech, TextToSpeech.QUEUE_FLUSH, null, null);
+ }
+ });
+ }else {
+ // If language other than English is selected, show a toast notification
+ Toast.makeText(getApplicationContext(), "Please note: Selected language may not be fully supported.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else {
+ //Log error if not found
+ Log.e(TAG, "FloatingActionButton not found");
}
- tts.setLanguage(locale);
- tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate());
- tts.setOnUtteranceProgressListener(utteranceListener);
+
+
+
+
+
}
+
}
+
+
+
+//Write a method that converts text to speech
+//
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 7b5a4d5b0..10d77ae7c 100644
--- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java
+++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.text.Spannable;
+import android.text.SpannableString;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -52,6 +53,7 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
private TTSManager voiceAnnouncement;
private TrackStatistics trackStatistics;
+// trackStatistics.toString(); to be done in the code
private static final Distance DISTANCE_OFF = Distance.of(Double.MAX_VALUE);
private Distance distanceFrequency = DISTANCE_OFF;
@@ -79,7 +81,7 @@ public VoiceAnnouncementManager(@NonNull Context context) {
public void start(@Nullable TrackStatistics trackStatistics) {
voiceAnnouncement = new TTSManager(context);
- voiceAnnouncement.start();
+ voiceAnnouncement.start(); // instance of TTSManager to call the start method which initializes the TTS engine
update(trackStatistics);
}
@@ -87,6 +89,12 @@ void update(@Nullable TrackStatistics trackStatistics) {
this.trackStatistics = trackStatistics;
updateNextDuration();
updateNextTaskDistance();
+// Create a Spannable object with the text from TrackStatistics.toString()
+// if(trackStatistics !=null){
+// Spannable announcement = new SpannableString(trackStatistics.toString());
+// // Pass the Spannable object to the announce() method of TTSManager
+// voiceAnnouncement.announce(announcement);
+ //}
}
private boolean shouldNotAnnounce() {
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 05179f1b7..c541d34cd 100644
--- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java
+++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java
@@ -13,6 +13,7 @@
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.TtsSpan;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -29,6 +30,7 @@
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
class VoiceAnnouncementUtils {
+ private static final String TAG2 = TTSManager.class.getSimpleName();
private VoiceAnnouncementUtils() {
}
@@ -77,6 +79,7 @@ static Spannable createStatistics(Context context, TrackStatistics trackStatisti
double distanceInUnit = totalDistance.toKM_Miles(unitSystem);
if (shouldVoiceAnnounceTotalDistance()) {
+ Log.d(TAG2, "in if statement");
builder.append(context.getString(R.string.total_distance));
// Units should always be english singular for TTS.
// See https://developer.android.com/reference/android/text/style/TtsSpan?hl=en#TYPE_MEASURE
diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java
index 41fa6d8e7..d1fa17894 100644
--- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java
+++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java
@@ -72,7 +72,8 @@ public TrackStatistics() {
*
* @param other another statistics data object to copy from
*/
- public TrackStatistics(TrackStatistics other) {
+ public TrackStatistics(TrackStatistics other) { // Need to create objects of this in order to activate
+ //VoiceAnnouncementsManager
startTime = other.startTime;
stopTime = other.stopTime;
totalDistance = other.totalDistance;
diff --git a/src/main/res/layout/track_stopped.xml b/src/main/res/layout/track_stopped.xml
index 4aca4c419..e18967c65 100644
--- a/src/main/res/layout/track_stopped.xml
+++ b/src/main/res/layout/track_stopped.xml
@@ -184,7 +184,7 @@
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
-
+