Android Xamarin网络视图上的摄像头
我有一个简单的Xamarin页面,其中的WebView调用WebRTC测试页面: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
_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 };
}
}