Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/203.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android Xamarin网络视图上的摄像头_Android_Xamarin_Webview_Webrtc_Android Permissions - Fatal编程技术网

Android Xamarin网络视图上的摄像头

Android Xamarin网络视图上的摄像头,android,xamarin,webview,webrtc,android-permissions,Android,Xamarin,Webview,Webrtc,Android Permissions,我有一个简单的Xamarin页面,其中的WebView调用WebRTC测试页面: _webView = new WebView { Source = "https://test.webrtc.org/", WidthRequest = 1000, HeightRequest = 1000 }; var stackLayout = new StackLayout

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

        _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 } };
该页面在同一个Android仿真器上的Chrome上运行良好,但在WebView上显示“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;
怎么了


感谢关于
NotAllowedError
,发件人:

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


您需要自定义
WebView
来覆盖
WebChromeClient
OnPermissionRequest
方法

MyWebView
PCL中的类:

public class MyWebView: WebView
{
}
MyWebViewRenderer
MyWebClient
类:

[assembly: ExportRenderer(typeof(App45.MyWebView), typeof(MyWebViewRenderer))]
namespace App45.Droid
{
    public class MyWebViewRenderer : WebViewRenderer
    {
        Activity mContext;
        public MyWebViewRenderer(Context context) : base(context)
        {
            this.mContext = context as Activity;
        }
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);
            Control.Settings.JavaScriptEnabled = true;
            Control.ClearCache(true);
            Control.SetWebChromeClient(new MyWebClient(mContext));
        }
        public class MyWebClient : WebChromeClient
        {
            Activity mContext;
            public MyWebClient(Activity context) {
                this.mContext = context;
            }
            [TargetApi(Value = 21)]
            public override void OnPermissionRequest(PermissionRequest request)
            {
                mContext.RunOnUiThread(() => {
                        request.Grant(request.GetResources());

                        });

            }
        }

    }

}
[程序集:ExportRenderer(typeof(App45.MyWebView)、typeof(MyWebViewRenderer))]
名称空间App45.Droid
{
公共类MyWebViewRenderer:WebViewRenderer
{
活动mContext;
公共MyWebViewRenderer(上下文):基础(上下文)
{
this.mContext=作为活动的上下文;
}
受保护的覆盖无效OnElementChanged(ElementChangedEventArgs e)
{
基础。一个要素发生变化(e);
Control.Settings.JavaScriptEnabled=true;
ClearCache(true);
SetWebChromeClient(新的MyWebClient(mContext));
}
公共类MyWebClient:WebChromeClient
{
活动mContext;
公共MyWebClient(活动上下文){
this.mContext=上下文;
}
[TargetApi(值=21)]
公共覆盖在PermissionRequest上无效(PermissionRequest请求)
{
mContext.RunOnUiThread(()=>{
request.Grant(request.GetResources());
});
}
}
}
}

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

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

根据提供给的OnShowFileChooser方法的FileChooser参数上的Android文档,CreateIntent()方法:

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

因此,尽管你的应用程序需要其他答案中注明的权限(读写外部存储器,以及摄像头权限),但如果你想提供摄像头作为选项,你必须自己构建它

对于Xamarin.Forms,您可以利用Xamarin.Essentials.MediaPicker API来避免直接处理Android意图、设置内容提供者等

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

以及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 };
        }
    }
公共类CameraFormsWebChromeClient:FormsWebChromeClient
{
串光路;
public override bool OnShowFileChooser(Android.Webkit.WebView WebView,Android.Webkit.IValueCallback filePathCallback,FileChooserParams FileChooserParams)
{
AlertDialog.Builder AlertDialog=新建AlertDialog.Builder(MainActivity.Instance);
alertDialog.SetTitle(“拍照或选择文件”);
alertDialog.SetNeutralButton(“拍照”,异步(发送方,alertArgs)=>
{
尝试
{
var photo=wait MediaPicker.CapturePhotoAsync();
var uri=await-LoadPhotoAsync(照片);
OnReceiveValue(uri);
}
catch(System.Exception-ex)
{
System.Console.WriteLine($“CapturePhotoAsync抛出:{ex.Message}”);
}
});
alertDialog.SetNegativeButton(“选择图片”,异步(发送方,alertArgs)=>
{
尝试
{
var photo=wait MediaPicker.PickPhotoAsync();
var uri=await-LoadPhotoAsync(照片);
OnReceiveValue(uri);
}
catch(System.Exception-ex)
{
System.Console.WriteLine($“PickPhotoAsync抛出:{ex.Message}”);
}
});
alertDialog.SetPositiveButton(“取消”,(发送方,alertArgs)=>
{
filePathCallback.OnReceiveValue(null);
});
Dialog=alertDialog.Create();
dialog.Show();
返回true;
}
异步任务LoadPhotoAsync(文件结果照片)
{
//取消
如果(照片==null)
{
_光程=零;
返回null;
}
//将文件保存到本地存储中
var newFile=Path.Combine(FileSystem.CacheDirectory,photo.FileName);
使用(var stream=await photo.OpenReadAsync())
使用(var newStream=System.IO.File.OpenWrite(newFile))
wait stream.CopyToAsync(新闻流);
_photoPath=newFile;
Android.Net.Uri=Android.Net.Uri.FromFile(新的Java.IO.File(_photoPath));
返回新的Android.Net.Uri[]{Uri};
}
    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 };
        }
    }