I have a Xamarin.Forms app, with an Android custom WebView. I also have a web application, this one can take a picture with camera.
When I use the mobile app with the WebView to navigate to the web site, it is not able to capture any image. Please look at the screenshot. This is the emulator screen. On the real phone, the image is white!
Noticed: On the emulator I can make it work by resetting the device, then it work again, something is random. On the real device, I can't make it work.
Of course, I have set and requested the access to the camera, but it does not help.
I have also test this page: https://test.webrtc.org/ But I got the following error: 'NotAllowedError'
But if you look at the code, you can see that I ask for the permission, and it is granted.
Here is the code:
public class HybridWebViewRenderer : WebViewRenderer
{
private const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
private Context _context;
public HybridWebViewRenderer(Context context) : base(context)
{
_context = context;
global::Android.Webkit.WebView.SetWebContentsDebuggingEnabled(true);
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
//---- Interop
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
((HybridWebView)Element).Cleanup();
}
if (e.NewElement != null)
{
Control.AddJavascriptInterface(new JsBridge(this), "jsBridge");
}
//---- Cookies
if (e.OldElement == null)
{
Control.Settings.AllowFileAccess = true;
Control.Settings.AllowFileAccessFromFileURLs = true;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
Control.SetWebViewClient(new CookieWebViewClient(Element as HybridWebView));
Control.SetWebChromeClient(new HybridWebChromeClient(_context));
}
}
}
internal class HybridWebChromeClient : WebChromeClient
{
private Context _context;
private MainActivity _mainActivity;
public HybridWebChromeClient(Context context)
{
_context = context;
_mainActivity = context as MainActivity;
}
[TargetApi(Value = 21)]
public override void OnPermissionRequest(PermissionRequest request)
{
((Android.App.Activity)_context).RunOnUiThread(() =>
{
request.Grant(request.GetResources());
});
}
public override bool OnShowFileChooser(Android.Webkit.WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
{
_mainActivity._uploadMessage = filePathCallback;
Intent i = new Intent(Intent.ActionGetContent);
i.AddCategory(Intent.CategoryOpenable);
i.SetType("*/*");
// The camera intent
Intent captureIntent = new Intent(Android.Provider.MediaStore.ActionImageCapture);
List<IParcelable> targetedShareIntents = new List<IParcelable>();
targetedShareIntents.Add(captureIntent);
captureIntent.AddCategory(Intent.ActionCameraButton);
//add camera intent to the main intent (i)
i.PutExtra(Intent.ExtraInitialIntents, targetedShareIntents.ToArray());
_mainActivity.StartActivityForResult(Intent.CreateChooser(i, "File Chooser"), 1);
return true;
}
//public override bool OnShowFileChooser(Android.Webkit.WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
//{
// _mainActivity.mUploadMessage = filePathCallback;
// Intent Fileintent = new Intent(Intent.ActionGetContent);
// Fileintent.AddCategory(Intent.CategoryOpenable);
// Fileintent.SetType("/");
// var intents = new List<Intent>();
// if (_context.PackageManager.HasSystemFeature(PackageManager.FeatureCameraAny))
// {
// Intent captureIntent = new Intent(Android.Provider.MediaStore.ActionImageCapture);
// var resolveinfolist = _context?.PackageManager?.QueryIntentActivities(captureIntent, 0);
// if (resolveinfolist != null && resolveinfolist.Count > 0)
// {
// var item = resolveinfolist[0];
// var packagename = item.ActivityInfo?.PackageName;
// var intent = new Intent(captureIntent);
// intent.SetComponent(new ComponentName(packagename, item.ActivityInfo.Name));
// intent.SetPackage(packagename);
// intent.AddFlags(ActivityFlags.GrantReadUriPermission);
// intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
// File storageDir = new File(Environment.ExternalStorageDirectory, "filename");
// var file = Android.OS.Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
// _mainActivity.ImageFile = storageDir;
// var uri = FileProvider.GetUriForFile(_context, _context.PackageName + ".provider", storageDir);
// intent.PutExtra(Android.Provider.MediaStore.ExtraOutput, uri);
// intents.Add(intent);
// }
// }
// intents.Add(Fileintent);
// var res = Intent.CreateChooser(intents[0], fileChooserParams.Title);
// intents?.RemoveAt(0);
// res.PutExtra(Intent.ExtraInitialIntents, intents?.ToArray());
// _mainActivity.StartActivityForResult(res, 1000);
// return true;
//}
}
internal class CookieWebViewClient : WebViewClient
{
private readonly HybridWebView _hybridWebView;
internal CookieWebViewClient(HybridWebView hybridWebView)
{
_hybridWebView = hybridWebView;
_hybridWebView.OnRequestNativeSetCookie += AppCookiesManager_OnSetCookie;
_hybridWebView.OnRequestNativeCookieRead += HybridWebView_OnRequestNativeCookieRead;
_hybridWebView.OnRequestNativeCookieWrite += HybridWebView_OnRequestNativeCookieWrite;
}
private void HybridWebView_OnRequestNativeCookieWrite(string url, CookieCollection cookies)
{
CookieManager.Instance.Flush();
}
private void HybridWebView_OnRequestNativeCookieRead(string url, CookieCollection cookies)
{
}
private void AppCookiesManager_OnSetCookie(Cookie cookie)
{
string cookieValue = cookie.Value;
string cookieDomain = cookie.Domain;
string cookieName = cookie.Name;
// Path
// Secure
// Expires
string currentCookie = CookieManager.Instance.GetCookie(cookieDomain) ?? "";
CookieManager.Instance.SetCookie(cookieDomain, cookieName + "=" + cookieValue + ";" + currentCookie);
CookieManager.Instance.Flush();
}
public override void OnPageFinished(global::Android.Webkit.WebView view, string url)
{
// Set all the cookies on the view
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
CookieSyncManager.Instance.Sync();
CookieManager.Instance.Flush();
CookieManager.AllowFileSchemeCookies();
CookieManager.SetAcceptFileSchemeCookies(true);
CookieManager.Instance.AcceptCookie();
CookieManager.Instance.AcceptThirdPartyCookies(view);
CookieManager.Instance.SetAcceptCookie(true);
CookieManager.Instance.SetAcceptThirdPartyCookies(view, true);
}
}
Also, I join the method requesting the permissions:
private async Task<bool> OnRequestCameraAccess()
{
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
}
//status = await Permissions.RequestAsync<Permissions.Media>();
//status = await Permissions.RequestAsync<Permissions.Microphone>();
//status = await Permissions.RequestAsync<Permissions.Photos>();
//status = await Permissions.RequestAsync<Permissions.StorageRead>();
//status = await Permissions.RequestAsync<Permissions.StorageWrite>();
return status == PermissionStatus.Granted;
}