Search code examples
c#xamarin.androidmaui

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


I am trying out Microsoft .NET MAUI that currently in Preview stage. I try to make a small Android app that will use Google voice recognizer service as a way to let user navigate the app. Just a small demo to see what can I do with it. This is also my first time to actually write a Xamarin/MAUI project, so I am not really sure what I can actually do wit the platform.

The problem is that I would like to have this Google service to always on (without timeout) or auto-close then re-open when timeout. In short, I want user to never actually have to deal with this screen:

enter image description here

My intention is that the will be a background thread to keep asking user to say the command, only stop when user do, and the service will always ready to receive the speech. However, I am unable to keep the above service always on or auto-close=>reopen when timeout.

I am search around and it seems that I cannot change the timeout of the service, so the only way is trying to auto-close=>reopen the service, but I don't know how to.

The below is my code, could you guy give me some direction with it?

1. The login page: only have username and password field, use will be asked to say the username. If it is exist, then asked to say password.

<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. The Speech Recognizer 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. MainActivity:

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. MainApplication

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)
        {
        }
    }
}

Solution

  • After struggle for a few days without any success, I found a new way to do this thing by using SpeechRecognizer class, instead of using a Google service. With this, I am able to have a better control on the process.

    To use SpeechRecognizer, I copied the code in "Create platform microphone services" for permission from this Microsoft page: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/azure-cognitive-services/speech-recognition

    I have update my code as below:

    1. Login page: currently is named 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. New Microphone Service to handle the permission
    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 to use SpeechRecognizer: Mostly take frrm this 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. Update MainActivities for permission
    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;
                }
            }
        }
    }
    

    Feel free to check out the code, but I will not use this for anything serious because it does not runs properly yet.

    Any opinion to improve for the code will be really appreciated, as I really want to get good with this MAUI platform.