MAUI-Android:如何防止 Google 语音识别器超时

MAUI-Android: How to keep Google Speech Recognizer from timeout

我正在试用目前处于预览阶段的 Microsoft .NET MAUI。 我尝试制作一个小型 Android 应用程序,它将使用 Google 语音识别器服务作为让用户导航应用程序的一种方式。只是一个小演示,看看我能用它做什么。这也是我第一次真正写一个Xamarin/MAUI项目,所以我不太确定我能用这个平台做什么。

问题是我希望此 Google 服务始终开启(无超时)或 auto-close 然后 re-open 超时。简而言之,我希望用户永远不必处理这个屏幕:

我的意图是,这将是一个后台线程,不断要求用户说出命令,只有在用户说出命令时才会停止,服务将始终准备好接收语音。 但是,我无法保持上述服务始终开启或auto-close=>超时时重新打开。

我正在搜索,似乎我无法更改服务的超时时间,所以唯一的方法是尝试 auto-close=> 重新打开服务,但我不知道如何。

下面是我的代码,你能给我一些指导吗?

1.登录页面: 只有用户名和密码字段,使用时会被要求说出用户名。如果存在,则要求说出密码。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiDemo.LoginPage"
             BackgroundColor="White">
    
    <ContentPage.Content>
        <StackLayout Margin="30" VerticalOptions="StartAndExpand">

            <Label 
                x:Name="lblTitle"
                HorizontalTextAlignment="Center"
                FontSize="Large"
                FontAttributes="Bold"
                />

            <Label/>
            
            <Button
                x:Name="btnSpeak"
                Text="Start"
                Clicked="btnSpeak_Clicked"
                FontAttributes="Bold"
                BackgroundColor="DarkGreen"
                />

            <Label/>

            <Label 
                x:Name="lblUsername"
                Text="Username" 
                FontAttributes="Bold"
                />
            <Entry
                x:Name="txtUsername"
                TextColor="Black"
                FontSize="18"
                VerticalOptions="StartAndExpand"
                HorizontalOptions="Fill" 
                IsReadOnly="True"
                />

            <Label/>

            <Label
                x:Name="lblPassword"
                Text="Password"
                FontAttributes="Bold"
                />
            <Entry
                x:Name="txtPassword"
                IsPassword="True"
                TextColor="Black"
                FontSize="18"
                VerticalOptions="StartAndExpand"
                HorizontalOptions="Fill" 
                IsReadOnly="True"
                />

            <Label/>

            <Label
                x:Name="lblDisplayname"
                Text="Name"
                FontAttributes="Bold"
                />
            <Label
                x:Name="txtDisplayname"
                />

            <Label/>

            <Label 
                x:Name="lblMessage"
                Text=""/>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>
using MauiDemo.Common;
using MauiDemo.Speech;
using Microsoft.Maui.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MauiDemo
{
    public partial class LoginPage : ContentPage
    {
        private string _field = string.Empty;
        private int _waitTime = 2000;

        public List<Language> Languages { get; }

        private SpeechToTextImplementation _speechRecongnitionInstance;

        private struct VoiceMode
        {
            int Username = 1;
            int Password = 2;
        }

        public LoginPage()
        {
            InitializeComponent();
            this.lblTitle.Text = "Login" + App.Status;

            CheckMicrophone();

            CommonData.CurrentField = string.Empty;

            try
            {
                _speechRecongnitionInstance = new SpeechToTextImplementation();
                _speechRecongnitionInstance.Language = DefaultData.SettingLanguage;
            }
            catch (Exception ex)
            {
                DisplayAlert("Error", ex.Message, "OK");
            }

            MessagingCenter.Subscribe<ISpeechToText, string>(this, "STT", (sender, args) =>
            {
                ReceivedUsernameAsync(args);
            });

            MessagingCenter.Subscribe<ISpeechToText>(this, "Final", (sender) =>
            {
                btnSpeak.IsEnabled = true;
            });

            MessagingCenter.Subscribe<IMessageSender, string>(this, "STT", (sender, args) =>
            {
                SpeechToTextRecievedAsync(args);
            });

            isReceiveUsername = false;
            isReceivePassword = false;
            RequestUsername();
        }

        protected override void OnDisappearing()
        {
            CommonData.CurrentField = string.Empty;
            base.OnDisappearing();
        }

        private async void btnSpeak_Clicked(Object sender, EventArgs e)
        {
            isReceiveUsername = false;
            isReceivePassword = false;
            await RequestUsername();
        }

        private async void SpeechToTextRecievedAsync(string args)
        {
            switch (_field)
            {
                case "Username":
                    await this.ReceivedUsernameAsync(args);
                    break;

                case "Password":
                    await this.ReceivedPasswordAsync(args);
                    break;

            }
        }

        bool isReceiveUsername = false;
        bool isReceivePassword = false;

        private async Task ReceivedUsernameAsync(string args)
        {
            txtUsername.Text = args.Replace(" ", string.Empty);
            lblMessage.Text = string.Empty;

            if (string.IsNullOrWhiteSpace(txtUsername.Text))
            {
                isReceiveUsername = false;
            }
            else
            {
                isReceiveUsername = true;
                var checkUser = DefaultData.Users.Where(x => x.Username.ToLower().Equals(txtUsername.Text.ToLower()));
                if (checkUser.Any())
                {
                    await RequestPassword();
                }
                else
                {
                    string message = CommonData.GetMessage(MessageCode.WrongUsername);
                    lblMessage.Text = message;
                    isReceiveUsername = false;
                    await RequestUsername(message);
                }
            }
        }

        private async Task ReceivedPasswordAsync(string args)
        {
            txtPassword.Text = args.Replace(" ", string.Empty);
            lblMessage.Text = string.Empty;

            if (string.IsNullOrWhiteSpace(txtPassword.Text))
            {
                isReceivePassword = false;
            }
            else
            {
                isReceivePassword = true;
                var checkUser = DefaultData.Users.Where(x => x.Username.ToLower().Equals(txtUsername.Text.ToLower()) && x.Password.Equals(txtPassword.Text));
                if (checkUser.Any())
                {
                    _field = "";
                    lblDisplayname.Text = checkUser.FirstOrDefault().Displayname;

                    string msg = CommonData.GetMessage(MessageCode.LoginSuccess);
                    await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                        msg
                        , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                        , speakRate: DefaultData.SettingSpeed
                        , pitch: DefaultData.SettingPitch
                        );

                    await Navigation.PushAsync(new MainPage());
                }
                else
                {
                    string message = CommonData.GetMessage(MessageCode.WrongPassword);
                    lblMessage.Text = message;
                    isReceivePassword = false;
                    await RequestPassword(message);
                }
            }
        }

        private async Task RepeatVoiceUsername(string message)
        {
            do
            {
                //_speechRecongnitionInstance.StopSpeechToText();
                //_speechRecongnitionInstance.StartSpeechToText();
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
                Thread.Sleep(_waitTime);
            }
            while (!isReceiveUsername);
        }

        private async Task RepeatVoicePassword(string message)
        {
            do
            {
                //_speechRecongnitionInstance.StopSpeechToText();
                //_speechRecongnitionInstance.StartSpeechToText();
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
                Thread.Sleep(_waitTime);
            }
            while (!isReceivePassword);
        }

        private bool CheckMicrophone()
        {
            string rec = Android.Content.PM.PackageManager.FeatureMicrophone;
            if (rec != "android.hardware.microphone")
            {
                // no microphone, no recording. Disable the button and output an alert
                DisplayAlert("Error", CommonData.GetMessage(MessageCode.SettingSaveSuccess), "OK");
                btnSpeak.IsEnabled = false;
                return false;
            }
            return true;
        }

        private async Task RequestUsername(string message = "")
        {
            _field = "Username";
            isReceiveUsername = false;
            txtUsername.Text = string.Empty;
            lblDisplayname.Text = string.Empty;
            txtUsername.Focus();
            message =  (message.IsNullOrWhiteSpace() ? CommonData.GetMessage(MessageCode.InputUsername) : message);

            Task.Run(() => RepeatVoiceUsername(message));
            
            _speechRecongnitionInstance.StartSpeechToText(_field);
        }

        private async Task RequestPassword(string message = "")
        {
            _field = "Password";
            isReceivePassword = false;
            txtPassword.Text = string.Empty;
            lblDisplayname.Text = string.Empty;
            txtPassword.Focus();
            message = (message.IsNullOrWhiteSpace() ? CommonData.GetMessage(MessageCode.InputPassword) : message);

            Task.Run(() => RepeatVoicePassword(message));

            _speechRecongnitionInstance.StartSpeechToText(_field);
        }
    }
}

2。语音识别器 class:

using Android.App;
using Android.Content;
using Android.Speech;
using Java.Util;
using Plugin.CurrentActivity;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MauiDemo.Speech
{
    public class SpeechToTextImplementation
    {
        public static AutoResetEvent autoEvent = new AutoResetEvent(false);
        private readonly int VOICE = 10;
        private Activity _activity;
        private float _timeOut = 3;
        private string _text;

        public SpeechToTextImplementation()
        {
            _activity = CrossCurrentActivity.Current.Activity;

        }
        public SpeechToTextImplementation(string text)
        {
            _text = text;
            _activity = CrossCurrentActivity.Current.Activity;
        }

        public string Language;

        public void StartSpeechToText()
        {
            StartRecordingAndRecognizing();
        }

        public void StartSpeechToText(string text)
        {
            _text = text;
            StartRecordingAndRecognizing();
        }

        private async void StartRecordingAndRecognizing()
        {
            string rec = global::Android.Content.PM.PackageManager.FeatureMicrophone;
            if (rec == "android.hardware.microphone")
            {
                try
                {
                    var locale = Locale.Default;
                    if (!string.IsNullOrWhiteSpace(Language))
                    {
                        locale = new Locale(Language);
                    }
                    Intent voiceIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
                    voiceIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);

                    voiceIntent.PutExtra(RecognizerIntent.ExtraPrompt, _text);
                    voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, _timeOut * 1000);
                    voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, _timeOut * 1000);
                    voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, _timeOut * 1000);
                    voiceIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);

                    voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, locale.ToString());
                    _activity.StartActivityForResult(voiceIntent, VOICE);

                    await Task.Run(() => { autoEvent.WaitOne(new TimeSpan(0, 2, 0)); });

                }
                catch (ActivityNotFoundException ex)
                {
                    String appPackageName = "com.google.android.googlequicksearchbox";
                    try
                    {
                        Intent intent = new Intent(Intent.ActionView, global::Android.Net.Uri.Parse("market://details?id=" + appPackageName));
                        _activity.StartActivityForResult(intent, VOICE);

                    }
                    catch (ActivityNotFoundException e)
                    {
                        Intent intent = new Intent(Intent.ActionView, global::Android.Net.Uri.Parse("https://play.google.com/store/apps/details?id=" + appPackageName));
                        _activity.StartActivityForResult(intent, VOICE);
                    }
                }

            }
            else
            {
                throw new Exception("No mic found");
            }
        }

        public void StopSpeechToText()
        {
            // Do something here to close the service
        }
    }
}

3。主要活动:

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Speech;
using MauiDemo.Common;
using Microsoft.Maui;
using Microsoft.Maui.Controls;

namespace MauiDemo
{
    [Activity(Label = "Maui Demo", Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
    public class MainActivity : MauiAppCompatActivity, IMessageSender
    {
        private readonly int VOICE = 10;

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {

            if (requestCode == VOICE)
            {
                if (resultCode == Result.Ok)
                {
                    var matches = data.GetStringArrayListExtra(RecognizerIntent.ExtraResults);
                    if (matches.Count != 0)
                    {
                        string textInput = matches[0];
                        MessagingCenter.Send<IMessageSender, string>(this, "STT", textInput);
                    }
                    else
                    {
                        MessagingCenter.Send<IMessageSender, string>(this, "STT", "");
                    }
                }
            }
            base.OnActivityResult(requestCode, resultCode, data);
        }
    }
}

4.主应用程序

using Android.App;
using Android.OS;
using Android.Runtime;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Plugin.CurrentActivity;
using System;

namespace MauiDemo
{
    [Application]
    public class MainApplication : MauiApplication
    {
        public MainApplication(IntPtr handle, JniHandleOwnership ownership)
            : base(handle, ownership)
        {
        }

        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
        public override void OnCreate()
        {
            base.OnCreate();
            CrossCurrentActivity.Current.Init(this);
        }

        public override void OnTerminate()
        {
            base.OnTerminate();
        }

        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityDestroyed(Activity activity)
        {
        }

        public void OnActivityPaused(Activity activity)
        {
        }

        public void OnActivityResumed(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
        {
        }

        public void OnActivityStarted(Activity activity)
        {
            CrossCurrentActivity.Current.Activity = activity;
        }

        public void OnActivityStopped(Activity activity)
        {
        }
    }
}

经过几天的努力没有任何成功,我找到了一种新的方法来完成这件事,即使用 SpeechRecognizer class,而不是使用 Google 服务。有了这个,我可以更好地控制流程。

为了使用 SpeechRecognizer,我复制了“创建平台麦克风服务”中的代码以获得此 Microsoft 页面的许可:https://docs.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/azure-cognitive-services/speech-recognition

我已经更新我的代码如下:

  1. 登录页面:当前名为 Prototype2。
using MauiDemo.Common;
using MauiDemo.Speech;
using Microsoft.Maui.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MauiDemo.View
{
    public partial class Prototype2 : ContentPage
    {
        private string _field = string.Empty;
        private int _waitTime = 2000;

        public List<Language> Languages { get; }

        private SpeechToTextImplementation2 _speechRecognizer;

        //private BackgroundWorker worker = new BackgroundWorker();

        private struct VoiceMode
        {
            int Username = 1;
            int Password = 2;
        }

        public Prototype2()
        {
            InitializeComponent();
            this.lblTitle.Text = "Prototype2" + App.Status;

            CheckMicrophone();

            CommonData.CurrentField = string.Empty;

            try
            {
                _speechRecognizer = new SpeechToTextImplementation2();
                _speechRecognizer.Language = DefaultData.SettingLanguage;
            }
            catch (Exception ex)
            {
                DisplayAlert("Error", ex.Message, "OK");
            }

            MessagingCenter.Subscribe<ISpeechToText, string>(this, "STT", (sender, args) =>
            {
                ReceivedUsernameAsync(args);
            });

            MessagingCenter.Subscribe<ISpeechToText>(this, "Final", (sender) =>
            {
                btnSpeak.IsEnabled = true;
            });

            MessagingCenter.Subscribe<IMessageSender, string>(this, "STT", (sender, args) =>
            {
                SpeechToTextRecievedAsync(args);
            });

            isReceiveUsername = false;
            isReceivePassword = false;
            RequestUsername(true);
        }

        protected override void OnDisappearing()
        {
            CommonData.CurrentField = string.Empty;
            base.OnDisappearing();
        }

        private async void btnSpeak_Clicked(Object sender, EventArgs e)
        {
            isReceiveUsername = false;
            isReceivePassword = false;
            await RequestUsername(true);
        }

        private async void SpeechToTextRecievedAsync(string args)
        {
            switch (_field)
            {
                case "Username":
                    await this.ReceivedUsernameAsync(args);
                    break;

                case "Password":
                    await this.ReceivedPasswordAsync(args);
                    break;

            }
        }

        bool isReceiveUsername = false;
        bool isReceivePassword = false;

        private async Task ReceivedUsernameAsync(string args)
        {
            txtUsername.Text = args.Replace(" ", string.Empty);
            lblMessage.Text = string.Empty;

            if (string.IsNullOrWhiteSpace(txtUsername.Text))
            {
                isReceiveUsername = false;
            }
            else
            {
                isReceiveUsername = true;
                var checkUser = DefaultData.Users.Where(x => x.Username.ToLower().Equals(txtUsername.Text.ToLower()));
                if (checkUser.Any())
                {
                    await RequestPassword(true);
                }
                else
                {
                    string message = CommonData.GetMessage(MessageCode.WrongUsername);
                    lblMessage.Text = message;
                    isReceiveUsername = false;
                    await RequestUsername(false, message);
                }
            }
        }

        private async Task ReceivedPasswordAsync(string args)
        {
            txtPassword.Text = args.Replace(" ", string.Empty);
            lblMessage.Text = string.Empty;

            if (string.IsNullOrWhiteSpace(txtPassword.Text))
            {
                isReceivePassword = false;
            }
            else
            {
                isReceivePassword = true;
                var checkUser = DefaultData.Users.Where(x => x.Username.ToLower().Equals(txtUsername.Text.ToLower()) && x.Password.Equals(txtPassword.Text));
                if (checkUser.Any())
                {
                    _field = "";
                    lblDisplayname.Text = checkUser.FirstOrDefault().Displayname;

                    string msg = CommonData.GetMessage(MessageCode.LoginSuccess);
                    await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                        msg
                        , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                        , speakRate: DefaultData.SettingSpeed
                        , pitch: DefaultData.SettingPitch
                        );

                    await Navigation.PushAsync(new MainPage());
                }
                else
                {
                    string message = CommonData.GetMessage(MessageCode.WrongPassword);
                    lblMessage.Text = message;
                    isReceivePassword = false;
                    await RequestPassword(false, message);
                }
            }
        }

        private async Task RepeatVoiceUsername(string message)
        {
            do
            {
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
                Thread.Sleep(_waitTime);
            }
            while (!isReceiveUsername);
        }

        private async Task RepeatVoicePassword(string message)
        {
            do
            {
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
                Thread.Sleep(_waitTime);
            }
            while (!isReceivePassword);
        }

        private bool CheckMicrophone()
        {
            string rec = Android.Content.PM.PackageManager.FeatureMicrophone;
            if (rec != "android.hardware.microphone")
            {
                // no microphone, no recording. Disable the button and output an alert
                DisplayAlert("Error", CommonData.GetMessage(MessageCode.SettingSaveSuccess), "OK");
                btnSpeak.IsEnabled = false;
                return false;
            }
            return true;
        }

        private async Task RequestUsername(bool isRepeat, string message = "")
        {
            _field = "Username";
            isReceiveUsername = false;
            //txtUsername.Text = string.Empty;
            //lblDisplayname.Text = string.Empty;
            txtUsername.Focus();
            message = (message.IsNullOrWhiteSpace() ? CommonData.GetMessage(MessageCode.InputUsername) : message);
            if (isRepeat)
            {
                Task.Run(() => RepeatVoiceUsername(message));
            }
            else
            {
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
            }
            _speechRecognizer.StartListening();
        }

        private async Task RequestPassword(bool isRepeat, string message = "")
        {
            _field = "Password";
            isReceivePassword = false;
            //txtPassword.Text = string.Empty;
            //lblDisplayname.Text = string.Empty;
            txtPassword.Focus();
            message = (message.IsNullOrWhiteSpace() ? CommonData.GetMessage(MessageCode.InputPassword) : message);
            if (isRepeat)
            {
                Task.Run(() => RepeatVoicePassword(message));
            }
            else
            {
                await Plugin.TextToSpeech.CrossTextToSpeech.Current.Speak(
                    message
                    , crossLocale: CommonData.GetCrossLocale(DefaultData.SettingLanguage)
                    , speakRate: DefaultData.SettingSpeed
                    , pitch: DefaultData.SettingPitch
                    );
            }
            _speechRecognizer.StartListening();
        }
    }
}
  1. 处理权限的新麦克风服务
using Android.App;
using Android.Content.PM;
using Android.OS;
using AndroidX.Core.App;
using Google.Android.Material.Snackbar;
using System.Threading.Tasks;

namespace MauiDemo.Speech
{
    public class MicrophoneService
    {
        public const int RecordAudioPermissionCode = 1;
        private TaskCompletionSource<bool> tcsPermissions;
        string[] permissions = new string[] { Manifest.Permission.RecordAudio };

        public MicrophoneService()
        {
            tcsPermissions = new TaskCompletionSource<bool>();
        }

        public Task<bool> GetPermissionAsync()
        {

            if ((int)Build.VERSION.SdkInt < 23)
            {
                tcsPermissions.TrySetResult(true);
            }
            else
            {
                var currentActivity = MainActivity.Instance;
                if (ActivityCompat.CheckSelfPermission(currentActivity, Manifest.Permission.RecordAudio) != (int)Permission.Granted)
                {
                    RequestMicPermissions();
                }
                else
                {
                    tcsPermissions.TrySetResult(true);
                }

            }

            return tcsPermissions.Task;
        }

        public void OnRequestPermissionResult(bool isGranted)
        {
            tcsPermissions.TrySetResult(isGranted);
        }

        void RequestMicPermissions()
        {
            if (ActivityCompat.ShouldShowRequestPermissionRationale(MainActivity.Instance, Manifest.Permission.RecordAudio))
            {
                Snackbar.Make(MainActivity.Instance.FindViewById(Android.Resource.Id.Content),
                        "Microphone permissions are required for speech transcription!",
                        Snackbar.LengthIndefinite)
                        .SetAction("Ok", v =>
                        {
                            ((Activity)MainActivity.Instance).RequestPermissions(permissions, RecordAudioPermissionCode);
                        })
                        .Show();
            }
            else
            {
                ActivityCompat.RequestPermissions((Activity)MainActivity.Instance, permissions, RecordAudioPermissionCode);
            }
        }
    }
}
  1. New Speech=>Text class 使用 SpeechRecognizer:主要采用 frrm 这个 How to increase the voice listen time in Google Recognizer Intent(Speech Recognition) Android
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Speech;
using AndroidX.Core.App;
using Java.Util;
using MauiDemo.Common;
using Microsoft.Maui.Controls;
using Plugin.CurrentActivity;
using System.Threading;

namespace MauiDemo.Speech
{

    public class SpeechToTextImplementation2 : Java.Lang.Object, IRecognitionListener, IMessageSender
    {
        public static AutoResetEvent autoEvent = new AutoResetEvent(false);
        private readonly int VOICE = 10;
        private Activity _activity;
        private float _timeOut = 3;
        private SpeechRecognizer _speech;
        private Intent _speechIntent;
        public string Words;
        public string Language;
        private MicrophoneService micService;

        public SpeechToTextImplementation2()
        {
            micService = new MicrophoneService();
            _activity = CrossCurrentActivity.Current.Activity;
            var locale = Locale.Default;
            if (!string.IsNullOrWhiteSpace(Language))
            {
                locale = new Locale(Language);
            }
            _speech = SpeechRecognizer.CreateSpeechRecognizer(this._activity);
            _speech.SetRecognitionListener(this);
            _speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
            _speechIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);

            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);

            _speechIntent.PutExtra(RecognizerIntent.ExtraLanguage, locale.ToString());

        }


        void RestartListening()
        {
            var locale = Locale.Default;
            if (!string.IsNullOrWhiteSpace(Language))
            {
                locale = new Locale(Language);
            }

            _speech.Destroy();
            _speech = SpeechRecognizer.CreateSpeechRecognizer(this._activity);
            _speech.SetRecognitionListener(this);
            _speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
            _speechIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, _timeOut * 1000);
            _speechIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);
            _speechIntent.PutExtra(RecognizerIntent.ExtraLanguage, locale.ToString());
            StartListening();
        }

        public async void StartListening()
        {
            bool isMicEnabled = await micService.GetPermissionAsync();
            if (!isMicEnabled)
            {
                Words = "Please grant access to the microphone!";
                return;
            }
            _speech.StartListening(_speechIntent);
        }

        public void StopListening()
        {
            _speech.StopListening();
        }

        public void OnBeginningOfSpeech()
        {

        }

        public void OnBufferReceived(byte[] buffer)
        {
        }

        public void OnEndOfSpeech()
        {

        }

        public void OnError([GeneratedEnum] SpeechRecognizerError error)
        {
            Words = error.ToString();
            MessagingCenter.Send<IMessageSender, string>(this, "Error", Words);
            RestartListening();
        }

        public void OnEvent(int eventType, Bundle @params)
        {
        }

        public void OnPartialResults(Bundle partialResults)
        {
        }

        public void OnReadyForSpeech(Bundle @params)
        {
        }

        public void OnResults(Bundle results)
        {

            var matches = results.GetStringArrayList(SpeechRecognizer.ResultsRecognition);
            if (matches == null)
                Words = "Null";
            else
                if (matches.Count != 0)
                Words = matches[0];
            else
                Words = "";

            MessagingCenter.Send<IMessageSender, string>(this, "STT", Words);

            RestartListening();
        }

        public void OnRmsChanged(float rmsdB)
        {

        }
    }
}
  1. 更新 MainActivities 以获得权​​限
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Speech;
using MauiDemo.Common;
using MauiDemo.Speech;
using Microsoft.Maui;
using Microsoft.Maui.Controls;

namespace MauiDemo
{
    [Activity(Label = "Maui Demo", Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
    public class MainActivity : MauiAppCompatActivity, IMessageSender
    {


        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            Instance = this;
            micService = new MicrophoneService();
        }


        private readonly int VOICE = 10;

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {

            if (requestCode == VOICE)
            {
                if (resultCode == Result.Ok)
                {
                    var matches = data.GetStringArrayListExtra(RecognizerIntent.ExtraResults);
                    if (matches.Count != 0)
                    {
                        string textInput = matches[0];
                        MessagingCenter.Send<IMessageSender, string>(this, "STT", textInput);
                    }
                    else
                    {
                        MessagingCenter.Send<IMessageSender, string>(this, "STT", "");
                    }

                    //SpeechToTextImplementation.autoEvent.Set();
                }
            }
            base.OnActivityResult(requestCode, resultCode, data);
        }

        MicrophoneService micService;
        internal static MainActivity Instance { get; private set; }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            // ...
            switch (requestCode)
            {
                case MicrophoneService.RecordAudioPermissionCode:
                    if (grantResults[0] == Permission.Granted)
                    {
                        micService.OnRequestPermissionResult(true);
                    }
                    else
                    {
                        micService.OnRequestPermissionResult(false);
                    }
                    break;
            }
        }
    }
}

随时检查代码,但我不会将它用于任何严重的事情,因为它还不能正常运行。

任何改进代码的意见都将不胜感激,因为我真的很想好好使用这个 MAUI 平台。