From b8e5636e360394037aa8f92880efdb752e6b68d2 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Sat, 8 Jun 2024 23:34:37 -0700 Subject: [PATCH 1/8] fix: background audio handling --- .../kotlin/com/rtirl/chat/MainActivity.kt | 136 +++++++++++++----- lib/models/tts.dart | 6 + lib/screens/home.dart | 3 + lib/volume_plugin.dart | 13 ++ 4 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 lib/volume_plugin.dart diff --git a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt index f391817fa..20108cce2 100644 --- a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt +++ b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt @@ -20,16 +20,29 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.util.Log import androidx.core.app.NotificationCompat +import android.media.AudioAttributes +import android.media.AudioManager +import android.media.AudioManager.OnAudioFocusChangeListener +import android.media.AudioFocusRequest +import android.os.Handler +import android.os.Looper - -class MainActivity : FlutterActivity() { +class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener { private var sharedData: String = "" - companion object { + private val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() - var methodChannel: MethodChannel? = null + private lateinit var audioManager: AudioManager + private lateinit var focusRequest: AudioFocusRequest + private val handler = Handler(Looper.getMainLooper()) + private var playbackDelayed = false + companion object { + var methodChannel: MethodChannel? = null const val NOTIFICATION_ID = 6853027 } @@ -37,6 +50,14 @@ class MainActivity : FlutterActivity() { super.onCreate(savedInstanceState) handleIntent() startNotificationService() + + audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager + focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run { + setAudioAttributes(audioAttributes) + setAcceptsDelayedFocusGain(true) + setOnAudioFocusChangeListener(this@MainActivity, handler) + build() + } } private fun startNotificationService() { @@ -44,6 +65,21 @@ class MainActivity : FlutterActivity() { startService(intent) } + override fun onAudioFocusChange(focusChange: Int) { + when (focusChange) { + AudioManager.AUDIOFOCUS_GAIN -> { + if (playbackDelayed) { + playbackDelayed = false + } + } + AudioManager.AUDIOFOCUS_LOSS, + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { + // Handle audio focus loss + } + } + } + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { val ttsPlugin = TextToSpeechPlugin(this) val ttsChannel = MethodChannel( @@ -52,8 +88,13 @@ class MainActivity : FlutterActivity() { ) val notificationChannel = MethodChannel( - flutterEngine.dartExecutor.binaryMessenger, - "tts_notifications" + flutterEngine.dartExecutor.binaryMessenger, + "tts_notifications" + ) + + val volumeChannel = MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + "volume_channel" ) methodChannel = notificationChannel @@ -64,22 +105,51 @@ class MainActivity : FlutterActivity() { Log.d("Notification called", call.method); - when(call.method) { - "dismissNotification" -> { - val intent = Intent(this, NotificationService::class.java) - intent.putExtra("action", "dismissNotification") - intent.putExtra("id", NOTIFICATION_ID) - startService(intent) - result.success(true) - } - "showNotification" -> { - val intent = Intent(this, NotificationService::class.java) - intent.putExtra("action", "showNotification") - startService(intent) - result.success(true) - } - else -> result.notImplemented() - } + when (call.method) { + "dismissNotification" -> { + val intent = Intent(this, NotificationService::class.java) + intent.putExtra("action", "dismissNotification") + intent.putExtra("id", NOTIFICATION_ID) + startService(intent) + result.success(true) + } + "showNotification" -> { + val intent = Intent(this, NotificationService::class.java) + intent.putExtra("action", "showNotification") + startService(intent) + result.success(true) + } + else -> result.notImplemented() + } + } + + volumeChannel.setMethodCallHandler { call, result -> + + Log.d("Volume called", call.method); + + when (call.method) { + + "tts_on" -> { + val res = audioManager.requestAudioFocus(focusRequest) + when (res) { + AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> { + methodChannel?.invokeMethod("audioFocus", "gained") + Log.d("Permisssion granted", "response granted") + } + AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> { + playbackDelayed = true + methodChannel?.invokeMethod("audioFocus", "delayed") + } + else -> methodChannel?.invokeMethod("audioFocus", "failed") + } + } + "tts_off" -> { + audioManager.abandonAudioFocusRequest(focusRequest) + methodChannel?.invokeMethod("audioFocus", "lost") + + } + else -> result.notImplemented() + } } ttsChannel.setMethodCallHandler(ttsPlugin) @@ -91,8 +161,8 @@ class MainActivity : FlutterActivity() { "set" -> { val intent = Intent(this, AudioService::class.java) intent.putStringArrayListExtra( - "urls", - ArrayList(call.argument>("urls") ?: listOf()) + "urls", + ArrayList(call.argument>("urls") ?: listOf()) ) intent.action = AudioService.ACTION_START_SERVICE startService(intent) @@ -108,24 +178,24 @@ class MainActivity : FlutterActivity() { } "hasPermission" -> { result.success( - Build.VERSION.SDK_INT < Build.VERSION_CODES.M || - Settings.canDrawOverlays(this) + Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + Settings.canDrawOverlays(this) ) } "requestPermission" -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - !Settings.canDrawOverlays(this) + !Settings.canDrawOverlays(this) ) { startActivityForResult( - Intent( - Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:$packageName") - ), 8675309 + Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:$packageName") + ), 8675309 ) } result.success( - Build.VERSION.SDK_INT < Build.VERSION_CODES.M || - Settings.canDrawOverlays(this) + Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + Settings.canDrawOverlays(this) ) } else -> result.notImplemented() diff --git a/lib/models/tts.dart b/lib/models/tts.dart index 44eb77cf4..84b0df8a6 100644 --- a/lib/models/tts.dart +++ b/lib/models/tts.dart @@ -17,6 +17,7 @@ import 'package:rtchat/models/messages/twitch/user.dart'; import 'package:rtchat/models/tts/language.dart'; import 'package:rtchat/models/tts/bytes_audio_source.dart'; import 'package:rtchat/models/user.dart'; +import 'package:rtchat/volume_plugin.dart'; import 'package:flutter_tts/flutter_tts.dart'; class TtsModel extends ChangeNotifier { @@ -152,6 +153,11 @@ class TtsModel extends ChangeNotifier { if (value) { _lastMessageTime = DateTime.now(); } + if (value) { + VolumePlugin.reduceVolumeOnTtsStart(); + } else { + VolumePlugin.increaseVolumeOnTtsStop(); + } say( SystemMessageModel( text: "Text to speech ${value ? "enabled" : "disabled"}"), diff --git a/lib/screens/home.dart b/lib/screens/home.dart index bb8d64077..544b9a7b6 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -24,6 +24,7 @@ import 'package:rtchat/models/tts.dart'; import 'package:rtchat/models/user.dart'; import 'package:rtchat/notifications_plugin.dart'; import 'package:rtchat/tts_plugin.dart'; +import 'package:rtchat/volume_plugin.dart'; import 'package:wakelock/wakelock.dart'; class ResizableWidget extends StatefulWidget { @@ -264,6 +265,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { "Text to speech disabled"); await TextToSpeechPlugin.disableTTS(); NotificationsPlugin.cancelNotification(); + VolumePlugin.reduceVolumeOnTtsStart(); } else { channelStreamController.stream .listen((currentChannel) { @@ -278,6 +280,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { updateChannelSubscription( "${userModel.activeChannel?.provider}:${userModel.activeChannel?.channelId}"); NotificationsPlugin.showNotification(); + VolumePlugin.increaseVolumeOnTtsStop(); NotificationsPlugin.listenToTTs(ttsModel); } } diff --git a/lib/volume_plugin.dart b/lib/volume_plugin.dart new file mode 100644 index 000000000..c1eea732b --- /dev/null +++ b/lib/volume_plugin.dart @@ -0,0 +1,13 @@ +import 'package:flutter/services.dart'; + +class VolumePlugin { + static const MethodChannel _channel = MethodChannel('volume_channel'); + + static Future reduceVolumeOnTtsStart() async { + await _channel.invokeMethod('tts_on'); + } + + static Future increaseVolumeOnTtsStop() async { + await _channel.invokeMethod('tts_off'); + } +} From 19ba377a7176d4e3450591c38b3f8e2bd5a3a87c Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Sun, 9 Jun 2024 01:59:11 -0700 Subject: [PATCH 2/8] fix: refactor handling --- .../kotlin/com/rtirl/chat/MainActivity.kt | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt index 20108cce2..1d39700e0 100644 --- a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt +++ b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt @@ -40,6 +40,7 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener private lateinit var focusRequest: AudioFocusRequest private val handler = Handler(Looper.getMainLooper()) private var playbackDelayed = false + private var wasDucking = false companion object { var methodChannel: MethodChannel? = null @@ -52,7 +53,7 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener startNotificationService() audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager - focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run { + focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK).run { setAudioAttributes(audioAttributes) setAcceptsDelayedFocusGain(true) setOnAudioFocusChangeListener(this@MainActivity, handler) @@ -68,14 +69,27 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener override fun onAudioFocusChange(focusChange: Int) { when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { + + if (wasDucking) { + methodChannel?.invokeMethod("audioVolume", 1.0) + wasDucking = false + } if (playbackDelayed) { playbackDelayed = false + } } - AudioManager.AUDIOFOCUS_LOSS, - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { - // Handle audio focus loss + + methodChannel?.invokeMethod("audioVolume", 0.3) + wasDucking = true + } + AudioManager.AUDIOFOCUS_LOSS, + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { + + if (!wasDucking) { + + } } } } @@ -100,11 +114,6 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener methodChannel = notificationChannel notificationChannel.setMethodCallHandler { call, result -> - - Log.d("NotificationService", "startForeground called"); - - Log.d("Notification called", call.method); - when (call.method) { "dismissNotification" -> { val intent = Intent(this, NotificationService::class.java) @@ -124,11 +133,7 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener } volumeChannel.setMethodCallHandler { call, result -> - - Log.d("Volume called", call.method); - when (call.method) { - "tts_on" -> { val res = audioManager.requestAudioFocus(focusRequest) when (res) { @@ -146,7 +151,6 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener "tts_off" -> { audioManager.abandonAudioFocusRequest(focusRequest) methodChannel?.invokeMethod("audioFocus", "lost") - } else -> result.notImplemented() } @@ -285,7 +289,7 @@ class TextToSpeechPlugin(context: Context) : MethodCallHandler { override fun onDone(utteranceId: String) { result.success(true) } - + override fun onError(utteranceId: String) { // Speech encountered an error // Handle errors as needed From c1444cd87071f406ff0256c262a9504a83e051cf Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Sun, 9 Jun 2024 16:39:31 -0700 Subject: [PATCH 3/8] fix: try better muting --- .../src/main/kotlin/com/rtirl/chat/MainActivity.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt index 1d39700e0..c23007de7 100644 --- a/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt +++ b/android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt @@ -69,26 +69,28 @@ class MainActivity : FlutterActivity(), AudioManager.OnAudioFocusChangeListener override fun onAudioFocusChange(focusChange: Int) { when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { - + if (wasDucking) { - methodChannel?.invokeMethod("audioVolume", 1.0) + methodChannel?.invokeMethod("audioVolume", 1.0) wasDucking = false } + if (playbackDelayed) { playbackDelayed = false - + + } else { + + methodChannel?.invokeMethod("audioVolume", 1.0) } } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { - methodChannel?.invokeMethod("audioVolume", 0.3) wasDucking = true } AudioManager.AUDIOFOCUS_LOSS, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { - if (!wasDucking) { - + } } } From 44a83a4d2eb789f0c66c0b22fe9f52f3a2c02359 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Sun, 9 Jun 2024 16:43:36 -0700 Subject: [PATCH 4/8] fix: package handling --- lib/models/tts.dart | 10 ++++++++-- lib/tts_plugin.dart | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/models/tts.dart b/lib/models/tts.dart index 84b0df8a6..9f3c74dd0 100644 --- a/lib/models/tts.dart +++ b/lib/models/tts.dart @@ -155,9 +155,8 @@ class TtsModel extends ChangeNotifier { } if (value) { VolumePlugin.reduceVolumeOnTtsStart(); - } else { - VolumePlugin.increaseVolumeOnTtsStop(); } + say( SystemMessageModel( text: "Text to speech ${value ? "enabled" : "disabled"}"), @@ -364,6 +363,9 @@ class TtsModel extends ChangeNotifier { await audioPlayer.setAudioSource(BytesAudioSource(bytes)); await audioPlayer.play(); await Future.delayed(audioPlayer.duration ?? const Duration()); + if (_pending.isEmpty) { + VolumePlugin.increaseVolumeOnTtsStop(); + } } } @@ -375,10 +377,14 @@ class TtsModel extends ChangeNotifier { void unsay(String messageId) { _pending.remove(messageId); + if (_pending.isEmpty) { + VolumePlugin.increaseVolumeOnTtsStop(); + } } void stop() { _pending.clear(); + VolumePlugin.increaseVolumeOnTtsStop(); } void updateFromJson(Map json) { diff --git a/lib/tts_plugin.dart b/lib/tts_plugin.dart index 371b148ea..fb7b66384 100644 --- a/lib/tts_plugin.dart +++ b/lib/tts_plugin.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:rtchat/main.dart'; import 'package:rtchat/notifications_plugin.dart'; +import 'package:rtchat/volume_plugin.dart'; class TextToSpeechPlugin { static const MethodChannel channel = MethodChannel('ttsPlugin'); @@ -84,6 +85,7 @@ class TTSQueue { } if (queue.isNotEmpty) { + VolumePlugin.reduceVolumeOnTtsStart(); final previous = queue.last; queue.addLast(element); await previous.completer.future; @@ -92,10 +94,18 @@ class TTSQueue { } await TextToSpeechPlugin.speak(text); completer.complete(); + + if (isEmpty) { + VolumePlugin.increaseVolumeOnTtsStop(); + } } else { queue.addLast(element); await TextToSpeechPlugin.speak(text); completer.complete(); + + if (isEmpty) { + VolumePlugin.increaseVolumeOnTtsStop(); + } } queue.remove(element); } From f209c9c4e7a275d15cbb134fb8713cc29f9f7de7 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:05:57 -0700 Subject: [PATCH 5/8] fix: plugin --- lib/volume_plugin.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/volume_plugin.dart b/lib/volume_plugin.dart index c1eea732b..dfe32d96a 100644 --- a/lib/volume_plugin.dart +++ b/lib/volume_plugin.dart @@ -1,13 +1,13 @@ import 'package:flutter/services.dart'; class VolumePlugin { - static const MethodChannel _channel = MethodChannel('volume_channel'); + static const MethodChannel channel = MethodChannel('volume_channel'); static Future reduceVolumeOnTtsStart() async { - await _channel.invokeMethod('tts_on'); + await channel.invokeMethod('tts_on'); } static Future increaseVolumeOnTtsStop() async { - await _channel.invokeMethod('tts_off'); + await channel.invokeMethod('tts_off'); } } From a6e9718153b4e351c7f2730e29a2e1250d1b88f5 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Mon, 10 Jun 2024 02:11:23 -0700 Subject: [PATCH 6/8] fix: test --- test/models/tts_test.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/models/tts_test.dart b/test/models/tts_test.dart index a63237dd7..da7c7331a 100644 --- a/test/models/tts_test.dart +++ b/test/models/tts_test.dart @@ -1,6 +1,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:rtchat/tts_plugin.dart'; +import 'package:rtchat/volume_plugin.dart'; void main() { final ttsQueue = TTSQueue(); @@ -16,11 +17,24 @@ void main() { (MethodCall method) async { return null; }); + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(VolumePlugin.channel, + (MethodCall method) async { + if (method.method == 'tts_on' || method.method == 'tts_off') { + return null; // Mock response for volume channel methods + } + throw MissingPluginException( + 'No implementation found for method ${method.method} on channel ${VolumePlugin.channel.name}'); + }); }); tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(TextToSpeechPlugin.channel, null); + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(VolumePlugin.channel, null); }); test('Speak adds elements to the queue', () async { From 6ab3f94f164805f18d8fe84f0f5646b672f31397 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Mon, 17 Jun 2024 01:43:16 -0700 Subject: [PATCH 7/8] chore: bump just_audio --- pubspec.lock | 12 ++++++------ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 4c6e0b2d1..430ba4c21 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -569,26 +569,26 @@ packages: dependency: "direct main" description: name: just_audio - sha256: b607cd1a43bac03d85c3aaee00448ff4a589ef2a77104e3d409889ff079bf823 + sha256: "5abfab1d199e01ab5beffa61b3e782350df5dad036cb8c83b79fa45fc656614e" url: "https://pub.dev" source: hosted - version: "0.9.36" + version: "0.9.38" just_audio_platform_interface: dependency: transitive description: name: just_audio_platform_interface - sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1 + sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790" url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.3.0" just_audio_web: dependency: transitive description: name: just_audio_web - sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70" + sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c" url: "https://pub.dev" source: hosted - version: "0.4.9" + version: "0.4.11" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b08d0713..e2a7d341f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: google_mobile_ads: ^4.0.0 in_app_purchase: ^3.1.13 intl: ^0.19.0 - just_audio: ^0.9.36 + just_audio: ^0.9.38 linkify: ^5.0.0 metadata_fetch: ^0.4.1 mobile_scanner: ^3.5.7 From 067eb94d9f8e8c88de96ac97707abba439107cb6 Mon Sep 17 00:00:00 2001 From: SputNikPlop <100245448+SputNikPlop@users.noreply.github.com> Date: Sun, 15 Sep 2024 00:23:30 -0700 Subject: [PATCH 8/8] fix: volume plugin --- lib/screens/home.dart | 65 ++----------------------------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 404e8d09f..26b700742 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -253,69 +253,6 @@ class _HomeScreenState extends State with TickerProviderStateMixin { }, ); }, - ); - }), - Consumer( - builder: (context, ttsModel, child) { - return IconButton( - icon: Icon( - !kDebugMode - ? (ttsModel.enabled - ? Icons.record_voice_over - : Icons.voice_over_off) - : (ttsModel.newTtsEnabled - ? Icons.record_voice_over - : Icons.voice_over_off), - ), - tooltip: AppLocalizations.of(context)!.textToSpeech, - onPressed: () async { - if (!kDebugMode) { - ttsModel.setEnabled(AppLocalizations.of(context)!, - ttsModel.enabled ? false : true); - } else { - // Toggle newTtsEnabled and notify listeners immediately - ttsModel.newTtsEnabled = !ttsModel.newTtsEnabled; - - if (!ttsModel.newTtsEnabled) { - updateChannelSubscription(""); - await TextToSpeechPlugin.speak( - "Text to speech disabled"); - await TextToSpeechPlugin.disableTTS(); - NotificationsPlugin.cancelNotification(); - VolumePlugin.reduceVolumeOnTtsStart(); - } else { - // Start listening to the stream before toggling newTtsEnabled - channelStreamController.stream - .listen((currentChannel) { - if (currentChannel.isEmpty) { - ttsModel.newTtsEnabled = false; - } - }); - await TextToSpeechPlugin.speak( - "Text to speech enabled"); - updateChannelSubscription( - "${userModel.activeChannel?.provider}:${userModel.activeChannel?.channelId}", - ); - NotificationsPlugin.showNotification(); - VolumePlugin.increaseVolumeOnTtsStop(); - NotificationsPlugin.listenToTts(ttsModel); - } - } - }, - ); - }, - ), - Consumer( - builder: (context, ttsModel, child) { - return IconButton( - icon: Icon( - !kDebugMode - ? (ttsModel.enabled - ? Icons.record_voice_over - : Icons.voice_over_off) - : (ttsModel.newTtsEnabled - ? Icons.record_voice_over - : Icons.voice_over_off), ), if (width > 256) Consumer( @@ -362,6 +299,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { "Text to speech disabled"); await TextToSpeechPlugin.disableTTS(); NotificationsPlugin.cancelNotification(); + VolumePlugin.reduceVolumeOnTtsStart(); } else { // Start listening to the stream before toggling newTtsEnabled channelStreamController.stream @@ -376,6 +314,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { "${userModel.activeChannel?.provider}:${userModel.activeChannel?.channelId}", ); NotificationsPlugin.showNotification(); + VolumePlugin.increaseVolumeOnTtsStop(); NotificationsPlugin.listenToTts(ttsModel); } }