feat: Stay in audio mode after end of audio
This commit is contained in:
parent
6671abebd8
commit
12b320dcf5
3 changed files with 57 additions and 35 deletions
|
|
@ -203,7 +203,8 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
selectedTokenNotifier.value = selectedToken;
|
||||
selectModeController.setPlayingToken(selectedToken?.text);
|
||||
|
||||
if (selectedToken != null) {
|
||||
if (selectedToken != null &&
|
||||
selectModeController.selectedMode.value != SelectMode.audio) {
|
||||
TtsController.tryToSpeak(
|
||||
selectedToken!.text.content,
|
||||
langCode: pangeaMessageEvent.messageDisplayLangCode,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
static const double buttonSize = 40.0;
|
||||
|
||||
StreamSubscription? _playerStateSub;
|
||||
final ValueNotifier<bool> _isPlayingNotifier = ValueNotifier(false);
|
||||
StreamSubscription? _audioSub;
|
||||
|
||||
MatrixState? matrix;
|
||||
|
|
@ -168,6 +169,7 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
matrix?.voiceMessageEventId.value = null;
|
||||
_audioSub?.cancel();
|
||||
_playerStateSub?.cancel();
|
||||
_isPlayingNotifier.dispose();
|
||||
controller.playTokenNotifier.removeListener(_playToken);
|
||||
super.dispose();
|
||||
}
|
||||
|
|
@ -225,19 +227,22 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
|
||||
Future<void> playAudio() async {
|
||||
final playerID = "${messageEvent.eventId}_button";
|
||||
final isPlaying = matrix?.audioPlayer != null &&
|
||||
matrix?.voiceMessageEventId.value == playerID &&
|
||||
matrix!.audioPlayer!.playerState.processingState !=
|
||||
ProcessingState.completed;
|
||||
|
||||
if (matrix?.audioPlayer != null &&
|
||||
matrix?.voiceMessageEventId.value == playerID) {
|
||||
// If the audio player is already initialized and playing the same message, pause it
|
||||
if (matrix!.audioPlayer!.playerState.playing) {
|
||||
await matrix!.audioPlayer!.pause();
|
||||
return;
|
||||
}
|
||||
// If the audio player is paused, resume it
|
||||
await matrix!.audioPlayer!.play();
|
||||
if (isPlaying) {
|
||||
matrix!.audioPlayer!.playerState.playing
|
||||
? await matrix!.audioPlayer!.pause()
|
||||
: await matrix!.audioPlayer!.play();
|
||||
return;
|
||||
}
|
||||
|
||||
_reloadAudio();
|
||||
}
|
||||
|
||||
Future<void> _reloadAudio({Duration? seek}) async {
|
||||
matrix?.audioPlayer?.dispose();
|
||||
matrix?.audioPlayer = AudioPlayer();
|
||||
matrix?.voiceMessageEventId.value = "${messageEvent.eventId}_button";
|
||||
|
|
@ -250,21 +255,12 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
_audioSub = matrix?.audioPlayer?.positionStream.listen(_onPlayAudio);
|
||||
|
||||
try {
|
||||
if (matrix?.audioPlayer != null &&
|
||||
matrix!.audioPlayer!.playerState.playing) {
|
||||
await matrix!.audioPlayer!.pause();
|
||||
return;
|
||||
} else if (matrix?.audioPlayer?.position != Duration.zero) {
|
||||
TtsController.stop();
|
||||
await matrix?.audioPlayer?.play();
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller.audioFile == null) {
|
||||
await controller.fetchAudio();
|
||||
}
|
||||
|
||||
if (controller.audioFile == null) return;
|
||||
|
||||
final (PangeaAudioFile pangeaAudioFile, File? audioFile) =
|
||||
controller.audioFile!;
|
||||
|
||||
|
|
@ -280,6 +276,11 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
}
|
||||
|
||||
TtsController.stop();
|
||||
|
||||
if (seek != null) {
|
||||
matrix!.audioPlayer!.seek(seek);
|
||||
}
|
||||
|
||||
await matrix?.audioPlayer?.play();
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
|
|
@ -303,13 +304,20 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
}
|
||||
|
||||
void _onUpdatePlayerState(PlayerState state) {
|
||||
if (state.processingState == ProcessingState.completed) {
|
||||
updateMode(null);
|
||||
final current = _isPlayingNotifier.value;
|
||||
if (!current &&
|
||||
state.processingState == ProcessingState.ready &&
|
||||
state.playing) {
|
||||
_isPlayingNotifier.value = true;
|
||||
} else if (current &&
|
||||
(!state.playing ||
|
||||
state.processingState == ProcessingState.completed)) {
|
||||
_isPlayingNotifier.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _playToken() {
|
||||
final token = controller.playTokenNotifier.value;
|
||||
final token = controller.playTokenNotifier.value.$1;
|
||||
|
||||
if (token == null ||
|
||||
controller.audioFile?.$1.tokens == null ||
|
||||
|
|
@ -321,10 +329,18 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
(t) => t.text == token,
|
||||
);
|
||||
|
||||
if (ttsToken != null && matrix?.audioPlayer != null) {
|
||||
final start = Duration(milliseconds: ttsToken.startMS);
|
||||
if (ttsToken == null) return;
|
||||
|
||||
final isPlaying = matrix?.audioPlayer != null &&
|
||||
matrix!.audioPlayer!.playerState.processingState !=
|
||||
ProcessingState.completed;
|
||||
|
||||
final start = Duration(milliseconds: ttsToken.startMS);
|
||||
if (isPlaying) {
|
||||
matrix!.audioPlayer!.seek(start);
|
||||
matrix!.audioPlayer!.play();
|
||||
} else {
|
||||
_reloadAudio(seek: start);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -381,13 +397,15 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
: theme.colorScheme.primaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: _SelectModeButtonIcon(
|
||||
mode: mode,
|
||||
loading:
|
||||
controller.isLoading && mode == selectedMode,
|
||||
playing: mode == SelectMode.audio &&
|
||||
matrix?.audioPlayer?.playerState.playing ==
|
||||
true,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _isPlayingNotifier,
|
||||
builder: (context, playing, __) =>
|
||||
_SelectModeButtonIcon(
|
||||
mode: mode,
|
||||
loading: controller.isLoading &&
|
||||
mode == selectedMode,
|
||||
playing: mode == SelectMode.audio && playing,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -93,8 +93,11 @@ class SelectModeController with LemmaEmojiSetter {
|
|||
ValueNotifier<(ConstructIdentifier, String)?>(null);
|
||||
|
||||
final StreamController contentChangedStream = StreamController.broadcast();
|
||||
ValueNotifier<PangeaTokenText?> playTokenNotifier =
|
||||
ValueNotifier<PangeaTokenText?>(null);
|
||||
|
||||
// Sometimes the same token is clicked twice. Setting it to the same value
|
||||
// won't trigger the notifier, so use the bool for force it to trigger.
|
||||
ValueNotifier<(PangeaTokenText?, bool)> playTokenNotifier =
|
||||
ValueNotifier<(PangeaTokenText?, bool)>((null, false));
|
||||
|
||||
void dispose() {
|
||||
selectedMode.dispose();
|
||||
|
|
@ -202,7 +205,7 @@ class SelectModeController with LemmaEmojiSetter {
|
|||
constructEmojiNotifier.value = (constructId, emoji);
|
||||
|
||||
void setPlayingToken(PangeaTokenText? token) =>
|
||||
playTokenNotifier.value = token;
|
||||
playTokenNotifier.value = (token, !playTokenNotifier.value.$2);
|
||||
|
||||
Future<void> fetchAudio() => _audioLoader.load();
|
||||
Future<void> fetchTranslation() => _translationLoader.load();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue