You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI Android WebView调用录音功能遇Permission denied错误求助

MAUI多项目中WebView加载带录音功能外部URL时的权限问题

在MAUI多项目应用中,使用WebView加载带有录音选项的外部URL,已为此功能创建自定义处理程序且该程序可被调用,但仍遇到权限错误:
Uncaught (in promise) NotAllowedError: Permission denied

共享项目代码

public class WebPagewithMenuItem : ContentPage
{
    private static string title = "";
    private static string hostUrl = "";
    private readonly CustomWebView browser = new CustomWebView();

    public WebPagewithMenuItem(string url, string token, string pageName, string hostUrlString)
    {
        title = pageName;
        hostUrl = hostUrlString;
        var html = setHtml(url, token);

        switch (DeviceInfo.Platform)
        {
            case var value when value == DevicePlatform.WinUI:
                break;

            case var value when value == DevicePlatform.iOS:
                break;

            default:
                browser.Source = html;
                browser.Navigating += Browser_Navigating;
                browser.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().SetEnableZoomControls(true).SetDisplayZoomControls(true);
                Content = browser;
                break;
        }
    }

    private void Browser_Navigating(object? sender, WebNavigatingEventArgs e)
    {
        var args = new DecisionHandlerDelegate();
        args.Cancel = e.Cancel;
        args.Uri = e.Url;
        WebView_OnNavigationStarted(sender, args);
    }

    private HtmlWebViewSource setHtml(string url, string token)
    {
        var html = new HtmlWebViewSource
        {
            Html = @"
                    <style>
                        .loader {
                          border: 4px solid #f3f3f3;
                          border-radius: 50%;
                          border-top: 4px solid #3498db;
                          width: 40px;
                          height: 40px;
                          -webkit-animation: spin 2s linear infinite; /* Safari */
                          animation: spin 2s linear infinite;
                            position: absolute;
                            left: calc(50% - 20px);
                            top: calc(50% - 20px);
                        }

                        /* Safari */
                        @-webkit-keyframes spin {
                          0% { -webkit-transform: rotate(0deg); }
                          100% { -webkit-transform: rotate(360deg); }
                        }

                        @keyframes spin {
                          0% { transform: rotate(0deg); }
                          100% { transform: rotate(360deg); }
                        }
                    </style>
                    <div class='loader'></div>
                    <form action='" + url + @"' method='post' target='_self' id='formIframe' style='display: none'>
                        <input type = 'hidden' name = 'token' value = '" + token + @"'>
                        <input type = 'submit' value = 'Do Stuff!'/>
                    </form>
                    <script>
                            document.getElementById('formIframe').submit();
                    </script> "
        };

        return html;
    }

    private async void WebView_OnNavigationStarted(object? sender, DecisionHandlerDelegate e)
    {
        string uriLower = e.Uri.ToLower();
        if (!uriLower.Contains("data:text/html;charset=utf-8;base64"))
        {
            if (uriLower.Contains("/externalauth/authaccess") == false && uriLower.Contains("/externalauth/mobilehost") == false && e.Uri.Contains("file:///") == false && e.Uri.Contains("about:blank") == false && uriLower.Contains("/help") == false && uriLower.Contains("errors/mobileerror") == false && uriLower.Contains("security/login") == false && uriLower.Contains("mvc=0") == false)
            {
                string urlValue = (System.Net.WebUtility.UrlDecode(e.Uri)).Replace(hostUrl, "..");
                string newPageURL = "";
                newPageURL = hostUrl + "/externalauth/mobilehost?url=" + System.Net.WebUtility.UrlEncode(urlValue);

                switch (DeviceInfo.Platform)
                {
                    case var value when value == DevicePlatform.WinUI:
                        break;

                    default:
                        var webview = (Microsoft.Maui.Controls.WebView)sender;
                        if (webview != null)
                        {
                            webview.Source = newPageURL;
                        }
                        break;
                }
            }
            else if (uriLower.Contains("errors/mobileerror"))
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    Navigation.RemovePage(this.Navigation.NavigationStack[this.Navigation.NavigationStack.Count - 1]);
                    Navigation.PopAsync();
                });
        
                e.Cancel = true;
                browser.IsVisible = false;
            }
            else if (uriLower.Contains("security/login"))
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    Navigation.RemovePage(this.Navigation.NavigationStack[this.Navigation.NavigationStack.Count - 1]);
                    Navigation.PopAsync();
                });

                e.Cancel = true;
                browser.IsVisible = false;
            }
        }
    }

    private void WebPagewithMenuItem_ChildRemoved(object? sender, ElementEventArgs e)
    {
        switch (DeviceInfo.Platform)
        {
            case var value when value == DevicePlatform.WinUI:
                var mainPage = ((NavigationPage)Application.Current.MainPage);
                if (mainPage != null)
                {
                    mainPage.ToolbarItems.Clear();
                }
                break;

            default:
                break;
        }
        ((NavigationPage)Application.Current.MainPage).ChildRemoved -= WebPagewithMenuItem_ChildRemoved;
    }
}

CustomWebView

public class CustomWebView : Microsoft.Maui.Controls.WebView
{
    
}

Android端代码

public class CustomWebViewHandler : WebViewHandler
{
    protected override void ConnectHandler(Android.Webkit.WebView platformView)
    {
        base.ConnectHandler(platformView);

        // Enable JavaScript and configure other settings
        platformView.Settings.JavaScriptEnabled = true;
        platformView.Settings.DomStorageEnabled = true;
        platformView.Settings.MediaPlaybackRequiresUserGesture = false;
        platformView.SetWebChromeClient(new CustomWebChromeClient());
    }

    protected override void DisconnectHandler(Android.Webkit.WebView platformView)
    {
        platformView.SetWebChromeClient(null);
        base.DisconnectHandler(platformView);
    }
}

public class CustomWebChromeClient : WebChromeClient
{
    public override void OnPermissionRequest(PermissionRequest request)
    {
        // Check if microphone access is included in the request
        if (request.GetResources().Contains(PermissionRequest.ResourceAudioCapture))
        {
            request.Grant(request.GetResources()); 
        }
        else
        {
            base.OnPermissionRequest(request);
        }
    }
}

MainActivity.cs

protected async override void OnResume()
{
    try
    {
        base.OnResume();

        PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Microphone>();

        if (status != PermissionStatus.Granted)
            await Permissions.RequestAsync<Permissions.Microphone>();
   }
}

MauiProgram(已注册自定义处理程序)

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseSharedMauiApp()
            .ConfigureMauiHandlers((handlers) =>
            {
                handlers.AddHandler(typeof(CustomWebView), typeof(CustomWebViewHandler));
            });
        return builder.Build();
    }
}

AndroidManifest已添加权限

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

内容的提问来源于stack exchange,提问作者D.madushanka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:04:59