2010-08-31 81 views
16

Empecé a escribir una aplicación que proporciona al usuario un formulario HTML a través de un WebView. Como el formulario no está bajo mi control, los datos cumplimentados pueden enviarse como solicitud GET o POST. Mi aplicación es necesaria para capturar los datos del formulario transportado, es decir, obtener un control sobre lo que se ingresó en los campos del formulario.¿es posible acceder a datos de formulario HTML PUBLICADOS a través de un WebView?

Usando una devolución de llamada adecuada desde WebViewClient como onPageLoaded(), es fácil capturar datos de formularios de una solicitud GET. Sin embargo, no puedo encontrar ningún método apropiado para permitir lo mismo para los datos POST, es decir, poder acceder al cuerpo del mensaje HTTP POST que contiene los datos del formulario. ¿Me falta una devolución de llamada relevante aquí o simplemente no hay forma de lograr el objetivo especificado con la API dada (incluso el último nivel 8)?

Suponiendo que no fuera posible, consideré reemplazar y ampliar partes de android.webkit para introducir un nuevo enlace de devolución de llamada que se pasa el cuerpo POST de alguna manera. De esa forma, mi aplicación podría enviarse con un navegador/WebViewClient personalizado que cumpla con la función deseada. Sin embargo, no pude encontrar ningún buen lugar para comenzar con el código y me alegraría cualquier sugerencia al respecto (en caso de que el enfoque parezca prometedor).

¡Gracias de antemano!

+0

En realidad, puede que haya encontrado una manera a mí mismo: Debe ser posible inyectar algo de JavaScript que analiza la campos de formulario cuando el usuario envía el formulario. La interfaz javascript-a-Java proporcionada debería ser capaz de transferir los datos rellenos nuevamente al espacio de Java para su posterior procesamiento. Probaremos ese enfoque e informaremos. Por supuesto, cualquier comentario adicional es bienvenido! –

Respuesta

22

Según lo indicado en mi propio comentario a la pregunta original, el enfoque de inyección de JavaScript funciona. Básicamente, lo que debe hacer es agregar un fragmento de código JavaScript al evento DOMsubmitir, hacer que analice los campos del formulario y devolver el resultado a una función registrada en Java.

ejemplo Código:

public class MyBrowser extends Activity { 
    private final String jsInjectCode = 
     "function parseForm(event) {" + 
     " var form = this;" + 
     " // make sure form points to the surrounding form object if a custom button was used" + 
     " if (this.tagName.toLowerCase() != 'form')" + 
     "  form = this.form;" +  
     " var data = '';" + 
     " if (!form.method) form.method = 'get';" + 
     " data += 'method=' + form.method;" + 
     " data += '&action=' + form.action;" +   
     " var inputs = document.forms[0].getElementsByTagName('input');" + 
     " for (var i = 0; i < inputs.length; i++) {" + 
     "   var field = inputs[i];" + 
     "   if (field.type != 'submit' && field.type != 'reset' && field.type != 'button')" + 
     "    data += '&' + field.name + '=' + field.value;" + 
     " }" + 
     " HTMLOUT.processFormData(data);" + 
     "}" + 
     "" + 
     "for (var form_idx = 0; form_idx < document.forms.length; ++form_idx)" + 
     " document.forms[form_idx].addEventListener('submit', parseForm, false);" +  
     "var inputs = document.getElementsByTagName('input');" + 
     "for (var i = 0; i < inputs.length; i++) {" + 
     " if (inputs[i].getAttribute('type') == 'button')" + 
     "  inputs[i].addEventListener('click', parseForm, false);" + 
     "}" + 
     ""; 

    class JavaScriptInterface { 
     @JavascriptInterface 
     public void processFormData(String formData) { 
      //added annotation for API > 17 to make it work 
      <do whatever you need to do with the form data> 
     } 
    } 

    onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.browser); 
     WebView browser = (WebView)findViewById(R.id.browser_window); 
     browser.getSettings().setJavaScriptEnabled(true); 
     browser.addJavascriptInterface(new JavaScriptInterface(), "HTMLOUT"); 
     browser.setWebViewClient(new WebViewClient() { 
     @Override 
     public boolean shouldOverrideUrlLoading(WebView view, String url) { 
      view.loadUrl(url); 
      return true; 
     } 

     @Override 
     public void onPageFinished(WebView view, String url) { 
      view.loadUrl("javascript:(function() { " + 
         MyBrowser.jsInjectCode + "})()"); 
     } 
} 

De manera informal, lo que esto hace es inyectar el código JavaScript personalizado (como un controlador de onsubmit) cada vez que una página termine de cargar. Al enviar un formulario, Javascript analizará los datos del formulario y lo transferirá a Java a través del objeto JavaScriptInterface.

Para analizar los campos del formulario, el código de Javascript agrega los controladores de formulario onsubmit y onclick. El primero puede manejar envíos de formularios canónicos a través de un botón de envío regular, mientras que el último trata con botones de envío personalizados, es decir, botones que hacen algo de magia Javascript adicional antes de llamar al form.submit().

Tenga en cuenta que el código JavaScript puede no ser perfecto: puede haber otros métodos para enviar un formulario que mi código insertado no pueda detectar. Sin embargo, estoy convencido de que el código inyectado se puede actualizar para hacer frente a tales posibilidades.

+0

'window.HTMLOUT.processFormData (data);' no funcionó para mí, pero solo 'HTMLOUT.processFormData (data);' funcionó para mí. – flexdroid

+0

nice .... gracias hombre – Noman

+0

bien, finalmente lo tengo. corrigió el código también –

6

La respuesta siempre da error, así que decidí hacer una aplicación más simple que también contó bien estructurado JavaScript (JS es decir, en un archivo):

En sus activos carpeta crear un archivo llamado inject.js con siguiente código interior:

document.getElementsByTagName('form')[0].onsubmit = function() { 
    var objPWD, objAccount, objSave; 
    var str = ''; 
    var inputs = document.getElementsByTagName('input'); 
    for (var i = 0; i < inputs.length; i++) { 
     if (inputs[i].name.toLowerCase() === 'username') { 
      objAccount = inputs[i]; 
     } else if (inputs[i].name.toLowerCase() === 'password') { 
      objPWD = inputs[i]; 
     } else if (inputs[i].name.toLowerCase() === 'rememberlogin') { 
      objSave = inputs[i]; 
     } 
    } 
    if(objAccount != null) { 
     str += objAccount.value; 
    } 
    if(objPWD != null) { 
     str += ' , ' + objPWD.value; 
    } 
    if(objSave != null) { 
     str += ' , ' + objSave.value; 
    } 
    window.AndroidInterface.processHTML(str); 
    return true; 
}; 

Este es el código JavaScript que usaremos para las inyecciones, se puede cambiar a cabo las sentencias if como mejor le parezca y usar tipos de cambio o los nombres.La devolución de llamada para Android es esta línea: window.AndroidInterface.processHTML(str);

Luego, su actividad/fragmento debería tener este aspecto:

public class MainActivity extends ActionBarActivity { 
    class JavaScriptInterface { 
     @JavascriptInterface 
     public void processHTML(String formData) { 
      Log.d("AWESOME_TAG", "form data: " + formData); 
     } 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     WebView webView = new WebView(this); 
     this.setContentView(webView); 

     // enable javascript 
     WebSettings webSettings = webView.getSettings(); 
     webSettings.setJavaScriptEnabled(true); 
     webView.addJavascriptInterface(new JavaScriptInterface(), "AndroidInterface"); 

     // catch events 
     webView.setWebViewClient(new WebViewClient(){ 
      @Override 
      public void onPageFinished(WebView view, String url) { 
       try { 
        view.loadUrl("javascript:" + buildInjection()); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

     webView.loadUrl("http://someurl.com"); 
    } 

    private String buildInjection() throws IOException { 
     StringBuilder buf = new StringBuilder(); 
     InputStream inject = getAssets().open("inject.js");// file from assets 
     BufferedReader in = new BufferedReader(new InputStreamReader(inject, "UTF-8")); 
     String str; 
     while ((str = in.readLine()) != null) { 
      buf.append(str); 
     } 
     in.close(); 

     return buf.toString(); 
    } 
+0

¿De dónde se obtiene el valor de la casilla de entrada –

Cuestiones relacionadas