question

estebanorellanaramirez avatar image
2 Votes"
estebanorellanaramirez asked DanJ-6174 commented

problem opening camera with WebView and input type file in Xamarin Forms only Android

Good day to all.

I have researched a lot about how to open the camera through a file type input through a WebView in the Android version (it is a Xamarin.Forms project)

I have tried two ways, the first one, using the PermissionsPlugin library and that of a CustomWebView with their respective Renderer and I have not been able to make it work

Este es mi CustomWebViewRenderer:

 public class CustomWebViewRenderer : WebViewRenderer
     {
         //Activity mContext;
    
         public CustomWebViewRenderer(Android.Content.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.Settings.DomStorageEnabled = true;
             Control.Settings.SetPluginState(WebSettings.PluginState.On);
    
             Control.ClearCache(true);
    
             CustomWebClient cwc = new CustomWebClient(MainActivity.Instance);
             Control.SetWebChromeClient(cwc);
         }
    
         public class CustomWebClient : WebChromeClient
         {
             Activity mActivity = null;
    
             public CustomWebClient(Activity activity)
             {
                 mActivity = activity;
             }
    
             [TargetApi(Value = 21)]
             public override void OnPermissionRequest(PermissionRequest request)
             {
                 if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
                 {
                     mActivity.RunOnUiThread(() =>
                     {
                         request.Grant(request.GetResources());
                     });
                 }
             }
         }
     }

This is my MainActivity.cs:

 public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
     {
         public static MainActivity Instance { get; private set; }
    
         protected override void OnCreate(Bundle savedInstanceState)
         {
             TabLayoutResource = Resource.Layout.Tabbar;
             ToolbarResource = Resource.Layout.Toolbar;
    
             base.OnCreate(savedInstanceState);
    
             Instance = this;
    
             Xamarin.Essentials.Platform.Init(this, savedInstanceState);
             CrossCurrentActivity.Current.Init(this, savedInstanceState);
             global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    
             CachedImageRenderer.Init(false);
             AnimationViewRenderer.Init();
             LoadApplication(new App());
         }
    
         public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
         {
             Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
             base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
             Plugin.Permissions.PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
         }
     }


and these are the permissions assigned in the AndroidManifest.xml:

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.CAMERA" />
 <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
 <uses-feature android:name="android.hardware.location" android:required="false" />
 <uses-feature android:name="android.hardware.location.gps" android:required="false" />
 <uses-feature android:name="android.hardware.location.network" android:required="false" />


Any information on how to achieve this would be greatly appreciated. I wait for a hand.

Best regards

dotnet-xamarin
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi ,

I have a same problem with android device , and I don't know what should i do.



0 Votes 0 ·
JarvanZhang-MSFT avatar image
1 Vote"
JarvanZhang-MSFT answered DanJ-6174 commented

Hello,​

Welcome to our Microsoft Q&A platform!

To choose a file in WebView, try to override the OnShowFileChooser method in the custom WebChromeClient class. Check the following code:

public class CustomWebChromeClient : WebChromeClient
{
    Activity mActivity = null;
    public CustomWebChromeClient(Activity activity)
    {
        mActivity = activity;
    }
    public override bool OnShowFileChooser(Android.Webkit.WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
    {
        MainActivity.mUploadCallbackAboveL = filePathCallback;

        //TakePhoto();
        File imageStorageDir = new File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures), "MyAppFolder");

        if (!imageStorageDir.Exists())
        {
            imageStorageDir.Mkdirs();
        }
        // Create camera captured image file path and name, add ticks to make it unique 
        var file = new File(imageStorageDir + File.Separator + "IMG_" + DateTime.Now.Ticks + ".jpg");

        MainActivity.imageUri = Android.Net.Uri.FromFile(file);

        //Create camera capture image intent and add it to the chooser
        var captureIntent = new Intent(MediaStore.ActionImageCapture);
        captureIntent.PutExtra(MediaStore.ExtraOutput, MainActivity.imageUri);

        var i = new Intent(Intent.ActionGetContent);
        i.AddCategory(Intent.CategoryOpenable);
        i.SetType("image/*");

        var chooserIntent = Intent.CreateChooser(i, "Choose image");
        chooserIntent.PutExtra(Intent.ExtraInitialIntents, new Intent[] { captureIntent });

        MainActivity.Instance.StartActivityForResult(chooserIntent, MainActivity.PHOTO_REQUEST);
        return true;
    }
}

MainActivity class

public class MainActivity : AppCompatActivity
{
    public static IValueCallback mUploadCallbackAboveL;
    public static Android.Net.Uri imageUri;
    public static MainActivity Instance;
    public static int PHOTO_REQUEST = 10023;

    public static IValueCallback mUploadMessage;
    public static int FILECHOOSER_RESULTCODE = 1;
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        // Set our view from the "main" layout resource
        Instance = this;
    }
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
    {
        if (requestCode == FILECHOOSER_RESULTCODE)
        {
            if (null == mUploadMessage) return;
            Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
            mUploadMessage.OnReceiveValue(result);
            mUploadMessage = null;
        }
        else if (requestCode == PHOTO_REQUEST)
        {
            Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;

            if (mUploadCallbackAboveL != null)
            {
                onActivityResultAboveL(requestCode, resultCode, intent);
            }
            else if (mUploadMessage != null)
            {
                mUploadMessage.OnReceiveValue(result);
                mUploadMessage = null;
            }
        }
    }
    private void onActivityResultAboveL(int requestCode, Result resultCode, Intent data)
    {
        if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null)
        {
            return;
        }
        Android.Net.Uri[] results = null;
        if (resultCode == Result.Ok)
        {
            results = new Android.Net.Uri[] { imageUri };
            results[0] = MainActivity.imageUri;
        }
        mUploadCallbackAboveL.OnReceiveValue(results);
        mUploadCallbackAboveL = null;

    }
}


Similar issue you could refer to: https://stackoverflow.com/questions/62656020/xamarin-android-webview-dont-open-camera

Best Regards,

Jarvan Zhang



If the response is helpful, please click "Accept Answer" and upvote it.

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.



· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello Jarvan Zhang,

Thanks for your help, I made the corrections and your code worked perfectly, but the image was not selected within the input type file.

I had to make a small correction which is the following:

In the CustomWebChromeClient class replace the line of code:

 File imageStorageDir = new File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures), "MyAppFolder");

for this:

 File imageStorageDir = new File(mActivity.GetExternalFilesDir(Android.OS.Environment.DirectoryPictures), "APP");

With that, the application worked properly.

Best regards and thank you very much for your help.

Esteban Orellana

1 Vote 1 ·

Hey Jarvan Zhang,
Thanks for posting this. Very helpful.
In my project, I have a webview with an input of type file. All the code you have posted above works beautifully. However, I am facing problem at the last bit. After camera takes the picture, when control returns to OnActivityResult, the resultCode is always Cancelled. And some how the picture file does not go up to file input control. I have tried the solution posted by Esteban Orellana. But did not work for me.
Need a little help to figure out what I could be missing here. Any help would be much appreciated.
thanks,
Dan-J

0 Votes 0 ·
AshySlashy87-8159 avatar image
1 Vote"
AshySlashy87-8159 answered ratnadeepsidhu-1641 commented

For the Camera it works great!

But not for the FileChooser

The requestCode on the OnActivityResult Method always return the Code from the PHOTO_REQUEST --> 10023

  protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
         {
             System.Console.WriteLine(" # onActivityResult # requestCode=" + requestCode + " # resultCode=" + resultCode); //ALWAYS RETURN 10023

any hints?

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

take a look at my "solution" below

0 Votes 0 ·

Please send
Htlm
Css
Javascript

And java code plze it is very helpful form me plze send me if u know

How camera works on webview in android studio

0 Votes 0 ·
AshySlashy87-8159 avatar image
0 Votes"
AshySlashy87-8159 answered

Don't know if this is the best way to handle this... but it works...

my changes:


 protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
         {
             System.Console.WriteLine(" # onActivityResult # requestCode=" + requestCode + " # Intent=" + intent.DataString);
    
             if (intent.DataString != null)
             {
                 if (null == mUploadCallbackAboveL) return;
                 Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
    
                 mUploadCallbackAboveL.OnReceiveValue(new Android.Net.Uri[] { result });
                 mUploadCallbackAboveL = null;
    
             }
             else if (requestCode == PHOTO_REQUEST)
             {
                 Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
                 if (mUploadCallbackAboveL != null)
                 {
                     onActivityResultAboveL(requestCode, resultCode, intent);
                 }
                 else if (mUploadCallbackAboveL != null)
                 {
                     mUploadCallbackAboveL.OnReceiveValue(result);
                     mUploadCallbackAboveL = null;
                 }
             }
         }
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

NicholasPoh-9405 avatar image
1 Vote"
NicholasPoh-9405 answered NicholasPoh-9405 edited

Here's a cleaned up version of OnActivityResult(). We need to call filePathCallback.onReceiveValue(null) to cancel the request in case the user tapped on "Back". This will allow a new request to be initiated. onActivityResultAboveL() can be removed.

     protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
     {
         if (requestCode == PHOTO_REQUEST)
         {
             if (resultCode == Result.Ok)
             {
                 Android.Net.Uri result = intent.Data ?? ImageUri;
                 if (UploadCallbackAboveL != null)
                 {
                     UploadCallbackAboveL.OnReceiveValue(new Android.Net.Uri[] { result });
                 }
             }
             else
             {
                 UploadCallbackAboveL.OnReceiveValue(null);
             }
             UploadCallbackAboveL = null;
         }
         else
         {
             base.OnActivityResult(requestCode, resultCode, intent);
         }
     }

My only confusion is why can't the uri be placed in the Data property of the captureIntent and is passed through a global variable ImageUri. When I tried setting the Data property, the chooser is not displayed and the default Files option is selected. Is there a better approach?

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

ChiragPokiya-7649 avatar image
0 Votes"
ChiragPokiya-7649 answered
 I have a simple Xamarin Page with a WebView of above code that calls a WebRTC page but camera not working.
    
 What's wrong? I am at a loss and could need some help/guidance.
    
 Thank you and Please look into my attached code.
    
    
 [WebViewDemo][1]
    
    
   [1]: https://github.com/projectrbkei/WebViewDemo



5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.