Xamarin WebView上的摄像头

时间:2018-05-27 15:10:47

标签: android xamarin webview webrtc android-permissions

我有一个简单的Xamarin页面,带有一个调用WebRTC测试页面的WebView:

        _webView = new WebView
        {
            Source = "https://test.webrtc.org/",
            WidthRequest = 1000,
            HeightRequest = 1000
        };

        var stackLayout = new StackLayout()
        {
            Orientation = StackOrientation.Vertical,
            Padding = new Thickness(5, 20, 5, 10),
            Children = { _webView }
        };

        Content = new StackLayout { Children = { stackLayout } };

https://test.webrtc.org/页面可以在同一个Android模拟器上的Chrome上运行正常,但不能在WebView上工作说“#An; NotAllowedError"。

该应用程序具有所需的权限。以下代码(使用Plugin.Permissions)返回true:

var statusCamera = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
var statusMicrophone = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Microphone);
return statusCamera == PermissionStatus.Granted && statusMicrophone == PermissionStatus.Granted;

出了什么问题?

由于

2 个答案:

答案 0 :(得分:2)

关于NotAllowedError,来自here

  

用户已指定不允许当前浏览实例访问该设备;或者用户拒绝访问当前会话;或者用户拒绝全局访问用户媒体设备。

您需要自定义WebView来覆盖WebChromeClient的{​​{1}}方法。

PCL中的

OnPermissionRequest课程:

MyWebView

public class MyWebView: WebView { } MyWebViewRenderer类:

MyWebClient

Here,我提供了一个演示供您测试。相机应该适合你。

答案 1 :(得分:0)

Android 的 WebChromeClient 默认为文件选择器提供 Intent。此默认选择器意图提供的内容因 Android 操作系统版本而异。在 Android 6 和 7 上,当您选择图库时,有一个打开相机的选项,但在更高的 Android 操作系统版本上,没有图库,也没有可用的相机选项。

根据关于 FileChooserParams 的 Android 文档,它提供给 WebChromeClient 的 OnShowFileChooser 方法,CreateIntent() 方法:

<块引用>

创建将启动文件选择器以进行文件选择的意图。 Intent 支持从设备上可用的简单文件源中选择文件。某些高级资源(例如,实时媒体捕获)可能不受支持,希望支持这些资源或更高级文件操作的应用程序应构建自己的 Intent。

因此,尽管您的应用需要其他答案中注明的权限(读取和写入外部存储以及相机权限),但如果您想提供相机作为选项,则必须自己构建。

对于 Xamarin.Forms,您可以利用 Xamarin.Essentials.MediaPicker API 来避免直接处理 Android Intent、设置 ContentProviders 等。

以下是您可以添加到自定义 Xamarin.Forms WebViewRenderer 的解决方案(使用 MediaPicker docs 中的代码):

[assembly: ExportRenderer(typeof(WebView), typeof(MyWebViewRenderer))]
namespace YourAppNameSpace.Droid 
{
    public class MyWebViewRenderer: WebViewRenderer
    {

        public MyWebViewRenderer(Context context) : base(context) { }

        protected override FormsWebChromeClient GetFormsWebChromeClient()
        {
            return new CameraFormsWebChromeClient();
        }
    }
}

还有 CameraFormsWebChromeClient 类:

    public class CameraFormsWebChromeClient : FormsWebChromeClient
    {
        string _photoPath;
        public override bool OnShowFileChooser(Android.Webkit.WebView webView, Android.Webkit.IValueCallback filePathCallback, FileChooserParams fileChooserParams)
        {

            AlertDialog.Builder alertDialog = new AlertDialog.Builder(MainActivity.Instance);
            alertDialog.SetTitle("Take picture or choose a file");
            alertDialog.SetNeutralButton("Take picture", async (sender, alertArgs) =>
            {
                try
                {
                    var photo = await MediaPicker.CapturePhotoAsync();
                    var uri = await LoadPhotoAsync(photo);
                    filePathCallback.OnReceiveValue(uri);
                }
                catch (System.Exception ex)
                {
                    System.Console.WriteLine($"CapturePhotoAsync THREW: {ex.Message}");
                }
            });
            alertDialog.SetNegativeButton("Choose picture", async (sender, alertArgs) =>
            {
                try
                {
                    var photo = await MediaPicker.PickPhotoAsync();
                    var uri = await LoadPhotoAsync(photo);
                    filePathCallback.OnReceiveValue(uri);
                }
                catch (System.Exception ex)
                {
                    System.Console.WriteLine($"PickPhotoAsync THREW: {ex.Message}");
                }
            });
            alertDialog.SetPositiveButton("Cancel", (sender, alertArgs) =>
            {
                filePathCallback.OnReceiveValue(null);
            });
            Dialog dialog = alertDialog.Create();
            dialog.Show();
            return true;
        }

        async Task<Android.Net.Uri[]> LoadPhotoAsync(FileResult photo)
        {
            // cancelled
            if (photo == null)
            {
                _photoPath = null;
                return null;
            }
            // save the file into local storage
            var newFile = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
            using (var stream = await photo.OpenReadAsync())
            using (var newStream = System.IO.File.OpenWrite(newFile))
                await stream.CopyToAsync(newStream);
            _photoPath = newFile;
            Android.Net.Uri uri = Android.Net.Uri.FromFile(new Java.IO.File(_photoPath));
            return new Android.Net.Uri[] { uri };
        }
    }

我只是使用原生 Android Dialog 进行选择或拍照选择。当然,您可以创建自己的 UI。最重要的是,您必须从 OnShowFileChooser 方法返回“true”,让 WebChromeClient 知道您将调用 filePathCallback 并提供结果。如果您返回 false,您将收到本机异常,因为 WebChromeClient 被告知此方法不会提供结果,因此它提供自己的 null 结果,我们会收到“Duplicate showFileChooser result”错误。您还需要保存拍摄的图片,然后向 filePathCallback.OnReceiveValue 方法提供 Android.Net.Uri[] 。并且如果从OnShowFileChooser返回true,则必须调用此回调,因此如果用户取消,则需要调用filePathCallback.OnReceiveValue(null);