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.
- Obfuscation en proyectos de Xamarin
- MonoDroid: Controlador de errores global
- Appcompat-v7: 21.0.0 ': No se encontró ningún recurso que coincida con el nombre dado: attr' android: actionModeShareDrawable '
- ¿Por qué Xamarin.Android reconstruye toda la solución al ejecutarlo?
- No se puede combinar el título personalizado con otras funciones de título
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?
- ¿Cómo detectar la salida de la aplicación en android?
- Xamarin.Android android: versionCode android: versionName - Desde Jenkins Build Server
- Android FragmentTab host y Fragmentos dentro de Fragmentos
- Golpe de Monodroid
- ¿Cómo obtengo la dirección MAC y el dispositivo Android con 6.0 o superior en c #?
- Uso del depurador de Microsoft con Xamarin Android
- Agregando $ ANDROID_HOME / tools a $ Path en ventanas
- Xamarin archivo Android camino a largo
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?