Mono para Android, el campo de entrada de WebView filechooser no funciona

Tengo la página web que utiliza para cargar archivos. El usuario selecciona el archivo con <input type="file" /> y presiona el botón submit, todo funciona bien. Ahora tengo que crear la aplicación de Android (en C # con mono para Android) que contiene de webview simple y tiene que trabajar como la versión web.

Pero tropecé con el problema – cuando hago clic en el botón de Choose file entonces el diálogo del archivo no se abre.

Yo googled este problema pocos días, pero no encontré ninguna solución. Parece que hay una solución en la plataforma Java , pero no funciona en C #.

¿Tiene alguna idea de cómo hacer que funcione?

Tengo una idea de cómo hacer que funcione. Parte de ella es bog-estándar "cómo atar un método virtual", y parte de él es puro mal absoluto.

En primer lugar, necesitamos un "intermediario". Dado que WebChromeClient no declara el método openFileChooser() , debemos declarar una versión que lo haga, denominada OpenFileWebChromeClient . Declara un método OpenFileChooser virtual y proporciona un enlace para que pueda ser anulado:

 using System; using Android.App; using Android.Content; using Android.Runtime; using Android.OS; using Android.Webkit; namespace Scratch.FileUpload { [Register ("android/webkit/WebChromeClient", DoNotGenerateAcw=true)] class OpenFileWebChromeClient : WebChromeClient { static IntPtr id_openFileChooser; [Register ("openFileChooser", "(Landroid/webkit/ValueCallback;)V", "GetOpenFileChooserHandler")] public virtual void OpenFileChooser (IValueCallback uploadMsg) { if (id_openFileChooser == IntPtr.Zero) id_openFileChooser = JNIEnv.GetMethodID (ThresholdClass, "openFileChooser", "(Landroid/webkit/ValueCallback;)V"); if (GetType () == ThresholdType) JNIEnv.CallVoidMethod (Handle, id_openFileChooser, new JValue (JNIEnv.ToJniHandle (uploadMsg))); else JNIEnv.CallNonvirtualVoidMethod (Handle, ThresholdClass, id_openFileChooser, new JValue (JNIEnv.ToJniHandle (uploadMsg))); } #pragma warning disable 0169 static Delegate cb_openFileChooser; static Delegate GetOpenFileChooserHandler () { if (cb_openFileChooser == null) cb_openFileChooser = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, IntPtr>) n_OpenFileChooser); return cb_openFileChooser; } static void n_OpenFileChooser (IntPtr jnienv, IntPtr native__this, IntPtr native_uploadMsg) { OpenFileWebChromeClient __this = Java.Lang.Object.GetObject<OpenFileWebChromeClient> (native__this, JniHandleOwnership.DoNotTransfer); var uploadMsg = Java.Lang.Object.GetObject<IValueCallback> (native_uploadMsg, JniHandleOwnership.DoNotTransfer); __this.OpenFileChooser (uploadMsg); } #pragma warning restore 0169 } } 

A continuación, dado que C # carece de clases internas anónimas, necesitamos una clase explícita, llamada MyOpenFileWebChromeClient aquí:

 namespace Scratch.FileUpload { class MyOpenFileWebChromeClient : OpenFileWebChromeClient { Action<IValueCallback> cb; public MyOpenFileWebChromeClient(Action<IValueCallback> cb) { this.cb = cb; } public override void OpenFileChooser (IValueCallback uploadMsg) { cb (uploadMsg); } } 

El puerto de actividad es idéntico a la entrada de blog a la que te MyOpenFileWebChromeClient , excepto que utiliza MyOpenFileWebChromeClient lugar de la clase interna anónima. También he actualizado alguna lógica para mostrar el URI que OnActivityResult() recibe:

 namespace Scratch.FileUpload { [Activity (Label = "Scratch.FileUpload", MainLauncher = true)] public class Activity1 : Activity { private WebView wv; private IValueCallback mUploadMessage; const int FilechooserResultcode = 1; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); wv = new WebView (this); wv.SetWebViewClient(new WebViewClient()); wv.SetWebChromeClient(new MyOpenFileWebChromeClient(uploadMsg => { mUploadMessage = uploadMsg; var intent = new Intent (Intent.ActionGetContent); intent.AddCategory(Intent.CategoryOpenable); intent.SetType("image/*"); StartActivityForResult(Intent.CreateChooser(intent, "File Chooser"), FilechooserResultcode); })); SetHtml(null); SetContentView(wv); } void SetHtml(string filename) { string html = @"<html> <body> <h1>Hello, world!</h1> <p>Input Box:</p> <input type=""file"" /> <p>URI: " + filename + @" </body> </html>"; wv.LoadData(html, "text/html", "utf-8"); } protected override void OnActivityResult (int requestCode, Result resultCode, Intent data) { base.OnActivityResult (requestCode, resultCode, data); if (requestCode == FilechooserResultcode) { if (mUploadMessage == null) return; var result = data == null || resultCode != Result.Ok ? null : data.Data; SetHtml(result.ToString()); mUploadMessage.OnReceiveValue(result); mUploadMessage = null; } } } } 

Lamentablemente, ahora es el momento para el acto de puro mal absoluto. El problema con la declaración anterior para MyOpenFileWebChromeClient es que no funcionará, por la misma razón que el blog de @Override no pudo usar @Override en la declaración de clase interna anónima: el android.jar que construye su aplicación no lo hace Declare el método openFileChooser() .

El proceso de generación generará envoltorios de Callable de Android , que deben contener código Java válido. El problema es que el código generado utiliza @Override para métodos sobreescritos y métodos de interfaz, lo que resulta en el Wrapper invocable de Android para MyOpenFileWebChromeClient :

 package scratch.fileupload; public class MyOpenFileWebChromeClient extends android.webkit.WebChromeClient { static final String __md_methods; static { __md_methods = "n_openFileChooser:(Landroid/webkit/ValueCallback;)V:GetOpenFileChooserHandler\n" + ""; mono.android.Runtime.register ("Scratch.FileUpload.MyOpenFileWebChromeClient, Scratch.FileUpload, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", MyOpenFileWebChromeClient.class, __md_methods); } @Override public void openFileChooser (android.webkit.ValueCallback p0) { n_openFileChooser (p0); } private native void n_openFileChooser (android.webkit.ValueCallback p0); java.util.ArrayList refList; public void monodroidAddReference (java.lang.Object obj) { if (refList == null) refList = new java.util.ArrayList (); refList.add (obj); } public void monodroidClearReferences () { if (refList != null) refList.clear (); } } 

Obviamente, el @Override en MyOpenFileWebChromeClient.openFileChooser() generará un error del compilador, así que ¿cómo hacemos esto funcionar? ¡Proporcionando nuestra propia anotación de @Override !

 package scratch.fileupload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } 

Coloque lo anterior en un archivo denominado Override.java , agréguelo al proyecto y establezca su acción Build en AndroidJavaSource .

El proyecto resultante funciona porque proporcionamos una anotación @Override personalizada en el mismo paquete que el tipo MyOpenFileWebChromeClient . (Esto requiere que conozca el nombre del paquete generado y que proporcione una anotación @Override por cada paquete para el que haga esto). Los tipos del mismo paquete tienen prioridad sobre los nombres importados, incluso los nombres procedentes de java.lang , por lo que nuestra anotación personalizada @Override no sólo compila, sino que es utilizada por el MyOpenFileWebChromeClient android MyOpenFileWebChromeClient wrapper en preferencia a la anotación java.lang.Override .

Yo dije que era puro mal absoluto, ¿no?

  • "Aapt.exe" salió con el código 1 al crear el proyecto Mono para Android
  • Archivado tamaño de archivo APK muy grande - Xamarin.Android
  • Circular ImageView en Xamarin
  • Estoy tratando de hacer mono android simple "hola mundo", pero no veo "hola mundo" en el emulador por qué?
  • Es libre de monocrómenos?
  • InflateException con TextInputLayout y AlertDialog
  • 'Jobject' no debe ser IntPtr.Zero al ajustar el texto textview en MonoDroid
  • Error: JNI ERROR (bug de la aplicación): se accede a la referencia global obsoleta
  • Creación de aplicaciones de mapas de muestras monodroide
  • Formularios de Xamarin Android Aplicación no obtener DeviceToken Parse SDK
  • Simulaciones de desarrollo de Android de MonoDroid
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.