11using System . Diagnostics . CodeAnalysis ;
2- using System . Globalization ;
32using System . Runtime . Versioning ;
43using Android . Content ;
54using Android . Runtime ;
65using Android . Speech ;
6+ using Java . Lang ;
7+ using Java . Util . Concurrent ;
78using Microsoft . Maui . ApplicationModel ;
9+ using Exception = System . Exception ;
810
911namespace CommunityToolkit . Maui . Media ;
1012
@@ -13,21 +15,20 @@ public sealed partial class OfflineSpeechToTextImplementation
1315{
1416 SpeechRecognizer ? speechRecognizer ;
1517 SpeechRecognitionListener ? listener ;
16- SpeechToTextState currentState = SpeechToTextState . Stopped ;
1718
1819 /// <inheritdoc />
1920 public SpeechToTextState CurrentState
2021 {
21- get => currentState ;
22+ get ;
2223 private set
2324 {
24- if ( currentState != value )
25+ if ( field != value )
2526 {
26- currentState = value ;
27- OnSpeechToTextStateChanged ( currentState ) ;
27+ field = value ;
28+ OnSpeechToTextStateChanged ( field ) ;
2829 }
2930 }
30- }
31+ } = SpeechToTextState . Stopped ;
3132
3233 /// <inheritdoc />
3334 public ValueTask DisposeAsync ( )
@@ -43,22 +44,24 @@ public ValueTask DisposeAsync()
4344 static Intent CreateSpeechIntent ( SpeechToTextOptions options )
4445 {
4546 var intent = new Intent ( RecognizerIntent . ActionRecognizeSpeech ) ;
46- intent . PutExtra ( RecognizerIntent . ExtraLanguagePreference , Java . Util . Locale . Default . ToString ( ) ) ;
47- intent . PutExtra ( RecognizerIntent . ExtraLanguageModel , RecognizerIntent . LanguageModelFreeForm ) ;
47+
48+ intent . PutExtra ( RecognizerIntent . ExtraLanguageModel , RecognizerIntent . LanguageModelFreeForm ) ;
4849 intent . PutExtra ( RecognizerIntent . ExtraCallingPackage , Application . Context . PackageName ) ;
4950 intent . PutExtra ( RecognizerIntent . ExtraPartialResults , options . ShouldReportPartialResults ) ;
50-
51- var javaLocale = Java . Util . Locale . ForLanguageTag ( options . Culture . Name ) . ToString ( ) ;
51+
52+ var javaLocale = Java . Util . Locale . ForLanguageTag ( options . Culture . Name ) . ToLanguageTag ( ) ;
5253 intent . PutExtra ( RecognizerIntent . ExtraLanguage , javaLocale ) ;
54+ intent . PutExtra ( RecognizerIntent . ExtraLanguagePreference , javaLocale ) ;
55+ intent . PutExtra ( RecognizerIntent . ExtraOnlyReturnLanguagePreference , javaLocale ) ;
5356
5457 return intent ;
5558 }
5659
57- static bool IsSpeechRecognitionAvailable ( ) => OperatingSystem . IsAndroidVersionAtLeast ( 33 ) && SpeechRecognizer . IsOnDeviceRecognitionAvailable ( Application . Context ) ;
60+ static bool IsSpeechRecognitionAvailable ( ) => OperatingSystem . IsAndroidVersionAtLeast ( 34 ) && SpeechRecognizer . IsOnDeviceRecognitionAvailable ( Application . Context ) ;
5861
5962 [ MemberNotNull ( nameof ( speechRecognizer ) , nameof ( listener ) ) ]
60- [ SupportedOSPlatform ( "Android33 .0" ) ]
61- void InternalStartListening ( SpeechToTextOptions options )
63+ [ SupportedOSPlatform ( "Android34 .0" ) ]
64+ async Task InternalStartListening ( SpeechToTextOptions options , CancellationToken token = default )
6265 {
6366 if ( ! IsSpeechRecognitionAvailable ( ) )
6467 {
@@ -68,14 +71,28 @@ void InternalStartListening(SpeechToTextOptions options)
6871 var recognizerIntent = CreateSpeechIntent ( options ) ;
6972
7073 speechRecognizer = SpeechRecognizer . CreateOnDeviceSpeechRecognizer ( Application . Context ) ;
71- speechRecognizer . TriggerModelDownload ( recognizerIntent ) ;
72-
7374 listener = new SpeechRecognitionListener ( this )
7475 {
7576 Error = HandleListenerError ,
7677 PartialResults = HandleListenerPartialResults ,
7778 Results = HandleListenerResults
7879 } ;
80+
81+ var recognitionSupportTask = new TaskCompletionSource < RecognitionSupport > ( ) ;
82+ speechRecognizer . CheckRecognitionSupport ( recognizerIntent , new Executor ( ) , new RecognitionSupportCallback ( recognitionSupportTask ) ) ;
83+ var recognitionSupportResult = await recognitionSupportTask . Task ;
84+ if ( ! recognitionSupportResult . InstalledOnDeviceLanguages . Contains ( options . Culture . Name ) )
85+ {
86+ if ( ! recognitionSupportResult . SupportedOnDeviceLanguages . Contains ( options . Culture . Name ) )
87+ {
88+ throw new NotSupportedException ( $ "Culture '{ options . Culture . Name } ' is not supported") ;
89+ }
90+
91+ var downloadLanguageTask = new TaskCompletionSource ( ) ;
92+ speechRecognizer . TriggerModelDownload ( recognizerIntent , new Executor ( ) , new ModelDownloadListener ( downloadLanguageTask ) ) ;
93+ await downloadLanguageTask . Task . WaitAsync ( token ) ;
94+ }
95+
7996 speechRecognizer . SetRecognitionListener ( listener ) ;
8097 speechRecognizer . StartListening ( recognizerIntent ) ;
8198 }
@@ -161,4 +178,47 @@ static void SendResults(Bundle? bundle, Action<string> action)
161178 action . Invoke ( matches [ 0 ] ) ;
162179 }
163180 }
181+ }
182+
183+ [ SupportedOSPlatform ( "Android33.0" ) ]
184+ class RecognitionSupportCallback ( TaskCompletionSource < RecognitionSupport > recognitionSupportTask ) : Java . Lang . Object , IRecognitionSupportCallback
185+ {
186+ public void OnError ( int error )
187+ {
188+ recognitionSupportTask . TrySetException ( new Exception ( error . ToString ( ) ) ) ;
189+ }
190+
191+ public void OnSupportResult ( RecognitionSupport recognitionSupport )
192+ {
193+ recognitionSupportTask . TrySetResult ( recognitionSupport ) ;
194+ }
195+ }
196+
197+ class Executor : Java . Lang . Object , IExecutor
198+ {
199+ public void Execute ( IRunnable ? command )
200+ {
201+ command ? . Run ( ) ;
202+ }
203+ }
204+
205+ class ModelDownloadListener ( TaskCompletionSource downloadLanguageTask ) : Java . Lang . Object , IModelDownloadListener
206+ {
207+ public void OnError ( SpeechRecognizerError error )
208+ {
209+ downloadLanguageTask . SetException ( new Exception ( error . ToString ( ) ) ) ;
210+ }
211+
212+ public void OnProgress ( int completedPercent )
213+ {
214+ }
215+
216+ public void OnScheduled ( )
217+ {
218+ }
219+
220+ public void OnSuccess ( )
221+ {
222+ downloadLanguageTask . SetResult ( ) ;
223+ }
164224}
0 commit comments