Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2916: Add tts on android #2950

Merged
merged 49 commits into from
Jan 28, 2025
Merged

2916: Add tts on android #2950

merged 49 commits into from
Jan 28, 2025

Conversation

bahaaTuffaha
Copy link
Contributor

@bahaaTuffaha bahaaTuffaha commented Oct 5, 2024

Short description

Adding text-to-speech functionality.

Proposed changes

  • TtsPlayer.tsx : deals with html content and it passes it to react-native-tts as sentences. used debounce function from lodash to handle rapid volume change. I added an expandPlayer state just to keep player expanded when changes from sentence to sentence. This component located at app level where it wraps around the NavigationContainer to be able to be floating at the bottom of the screen while scrolling.

  • useTtsPlayer.ts: Is a hook used to pass data (content, title, visibility, volume) from across the app to TtsPlayer.tsx using useContext.

  • Categories.tsx, News.tsx, Events.tsx each one is using the useTtsPlayer.ts hook to pass content and title.

  • Created a component Slider.tsx instead of adding more libraires and used the ones we have: react-native-reanimated & react-native-gesture-handler to create slider that can be used anywhere other than the volume.

  • Added ReadAloud HeaderButtonTitle to be selected from the kebab menu to show the player. This button will show up only in Integreat/IntegreatTestCms and at third level pages.

  • The TtsPlayer will stop when the app is in the background also if you back out from a 3rd level page.

  • Notes:

    • Not Tested on iOS !
    • Why I passed sentences to tts.speak()? due to react-native-tts doesn't have a functional pause also passing word by word will sound more robotic.
    • Persian is not supported at react-native-tts.
    • Pausing on words is not possible cause there is no way to know when its finished reading word and pausing is not supported for android.

Side effects

  • Categories.tsx, News.tsx, Events.tsx, Header.tsx

Testing

  • At integreat : Go to any 3rd level content in categories or news.
  • Open the kebab menu on the right and select Read aloud.
  • The player will show up press play to make it start reading content.

Resolved issues

Fixes: #2916


Copy link
Contributor

@LeandraH LeandraH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works pretty well so far! A couple of points about the functionality:

  • If I change the volume while it's playing, it starts re-reading the current fragment. If possible, I think it should keep reading at the same spot.
  • In using, I thought the volume bar was for updating the speed of reading. Maybe that's just me but maybe not, we should see if other people have that problem.
  • For Persian (and any other languages where it doesn't work), I think we should hide the button to read the text aloud, and close the overlay if it's open when someone switches languages.
  • In emulated iOS, no sound comes out and I get this warning: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {"1000":{},"1001":{},"1002":{},"1003":{},"1004":{},"1005":{},"1006":{},"1007":{},"1008":{},"1009":{},"1010":{},"1011":{},"1012":{},"1013":{},"1014":{},"1015":{},"1016":{},"1017":{},"1018":{},"1019":{},"1020":{},"1021":{},"1022":{},"1023":{},"1024":{},"1025":{},"1026":{},"1027":{},"1028":{},"1029":{},"1030":{},"1031":{},"1032":{},"1033":{},"1034":{},"1035":{},"1036":{},"1037":{},"1038":{},"1039":{},"1040":{},"1041":{},"1042":{},"1043":{},"1044":{},"1045":{},"1046":{},"1047":{},"1048":{},"1049":{},"...(truncated keys)...":451} But I guess you did say that it's not yet tested for iOS :D

As discussed, I only half-checked the PR, let me know when it's ready for review again :)

assets/icons/no-sound.svg Outdated Show resolved Hide resolved
native/src/utils/TtsPlayerUtils.ts Outdated Show resolved Hide resolved
native/src/utils/TtsPlayerUtils.ts Outdated Show resolved Hide resolved
release-notes/unreleased/2916-Add-TTS-on-android.yml Outdated Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
assets/icons/no-sound.svg Outdated Show resolved Hide resolved
Copy link
Contributor

@f1sh1918 f1sh1918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i couldn't test it since its crashing for me on emulator when i click play on Android 13

native/src/components/Header.tsx Outdated Show resolved Hide resolved
native/src/components/Header.tsx Outdated Show resolved Hide resolved
native/src/components/News.tsx Outdated Show resolved Hide resolved
native/src/components/RemoteContent.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@f1sh1918 f1sh1918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me on android. good work.
Since its a draft some improvements are missing i guess

image
Shouldn't here stand the title of what will be read?
Even add shadows/elevation for the container and the play button
image
The icons should switch i guess since the direction arrows are wrong

image

I think it would be better to create a fixed area for this player instead of overlapping content, but this may be also done in an additional tasks, since it will need some effort.

native/src/components/Slider.tsx Outdated Show resolved Hide resolved
Copy link
Member

@steffenkleinle steffenkleinle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not tested again :) Nice job!

@hauf-toni
Copy link

@hauf-toni Hmm, okay for me. What should happen then if the player is opened and a user then navigates to another site that does not have any content (e.g. just to the parent)? Do we hide the close the player then? Or just show the player as it is displayed at the moment?

Good catch @steffenkleinle – The player should be kept open but we should also offer feedback. This solution avoids abrupt ui changes, the user retains control and there is no unexpected closing behaviour:

➡️ The player remains open but is shown as disabled. If a user clicks anyway, it reads “There is nothing to read on this page” or similar. If the user navigates back to a page with content, the player resumes normal behavior.

Copy link
Contributor

@f1sh1918 f1sh1918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested in on android and works great so far with one issue.

Is it intended that first level pages can not be 'read aloud'?
Screenshot_20250122_170038

Design

Some general opinions. I would keep the size of playing and not playing container equal size. I think this jumping of the size is a bit annoying.
With more space we could even display the complete headline (title) maybe in two lines.
The alignment of the "play" button should be more left aligned to have a bigger spacing between the close button. Here it can easily happen that the close button will be touched instead of the play button.

These are all things that could be done in an additional design improvement task

native/src/components/Header.tsx Outdated Show resolved Hide resolved
native/src/components/TtsContainer.tsx Show resolved Hide resolved
native/src/components/TtsPlayer.tsx Outdated Show resolved Hide resolved
@@ -57,8 +60,6 @@ const Events = ({ cityModel, language, navigateTo, events, slug, refresh }: Even
}

if (slug) {
const event = events.find(it => it.slug === slug)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change was needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for useTtsPlayer and for less redundant code..
image

@bahaaTuffaha
Copy link
Contributor Author

Design

Some general opinions. I would keep the size of playing and not playing container equal size. I think this jumping of the size is a bit annoying.

I followed the design from here:
https://www.figma.com/design/cA4F2MwHs2LNGviOWUjfE9/Integreat-Tickets-Frontend?node-id=3874-441&t=fkqv8aniSlohmWPe-0

image

@f1sh1918
Copy link
Contributor

f1sh1918 commented Jan 22, 2025

Design

Some general opinions. I would keep the size of playing and not playing container equal size. I think this jumping of the size is a bit annoying.

I followed the design from here: https://www.figma.com/design/cA4F2MwHs2LNGviOWUjfE9/Integreat-Tickets-Frontend?node-id=3874-441&t=fkqv8aniSlohmWPe-0

image

yes but now there is not "Vorlesefunktion" but title which may be longer.
Sometimes you have to challenge some design if you see it in action. :)
I wouldn't expect a player is changing its size but thats just my opinion

Unfortunately its not working for ios. Player can't be stopped.
We should either disable it for ios or not merge it yet.
I try to find a solution in the meantime

Could be related to this but not sure
ak1394/react-native-tts#256

@steffenkleinle
Copy link
Member

Unfortunately its not working for ios. Player can't be stopped. We should either disable it for ios or not merge it yet. I try to find a solution in the meantime

Could be related to this but not sure ak1394/react-native-tts#256

There is a separate issue for iOS (#2917) and it is disabled on production (as the feature flag for everything but integreat-test-cms is set to false.

@f1sh1918
Copy link
Contributor

f1sh1918 commented Jan 23, 2025

Ok i put some investigation into this.

The issue is that the action Tts.stop() triggers different event depending on the platform.
On Android it sends the tts-cancel which is fine because we don't listen to that
On Ios it sends the tts-finish which should be sent only on "speak finish"
We listen to this to continue with the next sentence thats why it triggers automatically the next sentence on ios if you pause.
Since you can even on beta not stop the tts on ios i would disable it if we can not fix it.

I put also some effort in checking the native modules and thought this shouldn't be too difficult to patch but in the java and swift code the particular functions send the correct events. So i'm not pretty sure where is the issue and how to fix that

@f1sh1918
Copy link
Contributor

f1sh1918 commented Jan 23, 2025

Yes we can do that. As far as i can see this issue was even in older version (tested several), so i'm not sure if it will be fixed or we have to think about a different implementation.

It looks like this is an ios internal problem since ios15 as you can find here, so the bindings in the native modules are correct but ios internally uses the wrong methods.
Not sure if we can do anything about that and this issue is open since years :(
https://developer.apple.com/forums/thread/691347

@bahaaTuffaha
Copy link
Contributor Author

On Android it sends the tts-cancel which is fine because we don't listen to that

How is the behavior of tts-cancel can we use it for ios to keep the chain of speech going?

@f1sh1918
Copy link
Contributor

On Android it sends the tts-cancel which is fine because we don't listen to that

How is the behavior of tts-cancel can we use it for ios to keep the chain of speech going?

@bahaaTuffaha tts-cancel is never send since the didCancelSpeechUtterance from the AVSpeechSynthesize in ios is never trigged internally see the link i posted before. I added lots of comments in my pr and try to explain what happens there

One question i didn't get an answer was why the first level texts will not be read or why they are disabled for tts.

Copy link
Contributor

@f1sh1918 f1sh1918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works fine on android. great work 👍

As i mentioned i think we could think of some design improvements but thats my personal preference and not your issue :)

@steffenkleinle
Copy link
Member

One question i didn't get an answer was why the first level texts will not be read or why they are disabled for tts.

That was part of the original design and discussion of the issue. Perhaps @hauf-toni can shed some more light on this. Personally I also think we should perhaps change this decision and allow to read all content (not counting children or similar). I think otherwise it is not really obvious for users how the feature works.

@bahaaTuffaha
Copy link
Contributor Author

On Android it sends the tts-cancel which is fine because we don't listen to that

How is the behavior of tts-cancel can we use it for ios to keep the chain of speech going?

@bahaaTuffaha tts-cancel is never send since the didCancelSpeechUtterance from the AVSpeechSynthesize in ios is never trigged internally see the link i posted before. I added lots of comments in my pr and try to explain what happens there

One question i didn't get an answer was why the first level texts will not be read or why they are disabled for tts.

image

@steffenkleinle
Copy link
Member

Alright, I think everything clear here for now, lets get this merged. @bahaaTuffaha can you check the failing CI?

@bahaaTuffaha
Copy link
Contributor Author

Alright, I think everything clear here for now, lets get this merged. @bahaaTuffaha can you check the failing CI?

image

and what about this?

@steffenkleinle
Copy link
Member

Alright, I think everything clear here for now, lets get this merged. @bahaaTuffaha can you check the failing CI?

image

and what about this?

That is a separate issue/task, so we are good to merge this (if it is disabled on ios as suggested here: #2950 (comment))

@bahaaTuffaha bahaaTuffaha merged commit 08e3ab8 into main Jan 28, 2025
7 checks passed
@bahaaTuffaha bahaaTuffaha deleted the 2916-Add-TTS-on-android branch January 28, 2025 09:59
@hauf-toni
Copy link

That was part of the original design and discussion of the issue. Perhaps @hauf-toni can shed some more light on this. Personally I also think we should perhaps change this decision and allow to read all content (not counting children or similar). I think otherwise it is not really obvious for users how the feature works.

Yes, I agree. This enhances predictability & clarity for users and they don't have to guess, when content is likely to be read aloud. What is the status of the design decision concerning the sizing, do you still need some input on this? @bahaaTuffaha

@steffenkleinle
Copy link
Member

That was part of the original design and discussion of the issue. Perhaps @hauf-toni can shed some more light on this. Personally I also think we should perhaps change this decision and allow to read all content (not counting children or similar). I think otherwise it is not really obvious for users how the feature works.

Yes, I agree. This enhances predictability & clarity for users and they don't have to guess, when content is likely to be read aloud. What is the status of the design decision concerning the sizing, do you still need some input on this? @bahaaTuffaha

@hauf-toni we have a new issue for that, see #3064. I think @f1sh1918 said he'll take a look at it, so perhaps you can discuss the necessary changes (in the new issue)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add TTS on android
5 participants