diff --git a/activities/EbookReader.activity/css/activity.css b/activities/EbookReader.activity/css/activity.css
index a0208fc0b8..122be3c367 100644
--- a/activities/EbookReader.activity/css/activity.css
+++ b/activities/EbookReader.activity/css/activity.css
@@ -349,3 +349,23 @@ body {
box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5);
border: 0px;
}
+
+#read-button {
+ background-image: url("../icons/read.svg");
+ background-size: contain;
+ background-repeat: no-repeat;
+ width: 47px;
+ height: 47px;
+ border: none;
+ outline: none;
+}
+
+#read-play {
+ background-image: url("../icons/read-play.svg");
+ background-size: contain;
+ background-repeat: no-repeat;
+ width: 47px;
+ height: 47px;
+ border: none;
+ outline: none;
+ }
\ No newline at end of file
diff --git a/activities/EbookReader.activity/icons/read-play.svg b/activities/EbookReader.activity/icons/read-play.svg
new file mode 100644
index 0000000000..772bab3e6a
--- /dev/null
+++ b/activities/EbookReader.activity/icons/read-play.svg
@@ -0,0 +1,23 @@
+
diff --git a/activities/EbookReader.activity/icons/read.svg b/activities/EbookReader.activity/icons/read.svg
new file mode 100644
index 0000000000..e1e2a5f81a
--- /dev/null
+++ b/activities/EbookReader.activity/icons/read.svg
@@ -0,0 +1,53 @@
+
+
+
+
+
diff --git a/activities/EbookReader.activity/js/activity.js b/activities/EbookReader.activity/js/activity.js
index e8988ed952..f15ff5ed0b 100644
--- a/activities/EbookReader.activity/js/activity.js
+++ b/activities/EbookReader.activity/js/activity.js
@@ -15,7 +15,10 @@ var app = new Vue({
currentEpub: null,
currentView: LibraryViewer,
currentLibrary: {database: []},
- timer: null
+ timer: null,
+ isSpeaking: false,
+ isPaused: false,
+ utterance: null
},
created: function() {
@@ -59,6 +62,8 @@ var app = new Vue({
});
});
+ this.$refs.toolbar.$on("read-aloud-clicked", this.toggleReadAloud);
+
// Handle resize
window.addEventListener("resize", function() {
vm.onResize();
@@ -235,6 +240,64 @@ var app = new Vue({
}
},
+ toggleReadAloud: function() {
+
+ if (!('speechSynthesis' in window)) {
+ alert("Text-to-Speech is not supported in your browser.");
+ return;
+ }
+
+
+ if (!this.utterance) {
+
+ let iframe = document.querySelector("#area iframe");
+ if (!iframe || !iframe.contentDocument) {
+ alert("Content not available for reading.");
+ return;
+ }
+
+
+ let storyText = iframe.contentDocument.body.innerText;
+ console.log("Extracted full iframe text:", storyText);
+
+ if (!storyText || storyText.trim() === "") {
+ alert("No text available for narration.");
+ return;
+ }
+
+ this.utterance = new SpeechSynthesisUtterance(storyText);
+ this.utterance.rate = 1.0;
+ this.utterance.pitch = 1.0;
+ this.utterance.onend = () => {
+
+ this.isSpeaking = false;
+ this.isPaused = false;
+ this.utterance = null;
+ };
+ }
+
+
+ if (!this.isSpeaking && !this.isPaused) {
+
+ window.speechSynthesis.speak(this.utterance);
+ this.isSpeaking = true;
+ this.isPaused = false;
+ console.log("Speech started from the beginning");
+ } else if (this.isSpeaking && !this.isPaused) {
+
+ window.speechSynthesis.pause();
+ this.isSpeaking = false;
+ this.isPaused = true;
+ console.log("Speech paused");
+ } else if (!this.isSpeaking && this.isPaused) {
+
+ window.speechSynthesis.resume();
+ this.isSpeaking = true;
+ this.isPaused = false;
+ console.log("Speech resumed from paused position");
+ }
+ },
+
onHelp: function() {
var options = {};
options.switchbutton = this.$refs.toolbar.$refs.switchbutton.$el;
@@ -278,7 +341,12 @@ var app = new Vue({
},
onStop: function() {
+
+ window.speechSynthesis.cancel();
+ this.utterance = null;
+ this.isSpeaking = false;
+ this.isPaused = false;
this.saveContextToJournal();
- }
+ }
}
});
diff --git a/activities/EbookReader.activity/js/toolbar.js b/activities/EbookReader.activity/js/toolbar.js
index 564f3db5ca..7d4fd3a745 100644
--- a/activities/EbookReader.activity/js/toolbar.js
+++ b/activities/EbookReader.activity/js/toolbar.js
@@ -27,7 +27,10 @@ var Toolbar = {