El prototipo de Panel contaba con una serie de requisitos:
- Permitir la compatibilidad con dispositivos Android e iOS (iPhone).
- Establecer conexión con un servicio web capaz de devolver una información solicitada (en concreto, la información devuelta consistiría en un fichero PDF a partir de los parámetros de la petición).
- Gestionar el fichero devuelto por el servicio web una vez volcado en el dispositivo.
- Hacer uso de alguna de las capacidades hardware del dispositivo (en concreto, de la cámara del dispositivo para el escaneo de códigos BIDI).
- Interactuar con otras aplicaciones del dispositivo (solicitando la visualización del PDF a cualquier aplicación del dispotivo capaz de visualizarlo).
El primero de los problemas a resolver fue una toma de decisiones acerca de cual sería la solución tecnológica que nos garantizara un máximo grado de compatibilidad de lo desarrollado entre tipos de dispositivos Android e iOS, con el mínimo esfuerzo. Y finalmente se optó por la siguiente arquitectura:
- Una primera capa javascript con jquery que cumpliría con el deseo de un código general único y con el requisito de compatibilidad entre dispositivos de distinto tipo (pantallas de usuarios y componentes dinámicos, navegación y operativa, llamada a servicios web, principalmente).
- Una segunda capa puente entre el código javascript y el núcleo de la funcionalidad. Este núcleo de la funcionalidad es en definitiva el que controla el comportamiento predefinido del dispositivo y recae en unas librerías específicas de cada tecnología. La capa puente se implementaría con las utilidades Phone-GAP.
- En paralelo a la capa anterior una capa de servicios web sobre la que se implementaría la transferencia de información entre un servidor que sirviera de repositorio de los ficheros PDF y el dispositivo móvil.
Despejado este primer problema, el trabajo de implementación consistía básicamente en abordar desarrollos por un lado a nivel de interfaz (implementando un flujo de trabajo y una navegación sencilla en javascript con llamadas tipo jquery), y por otro, implementando las llamadas a los web service y finalmente a un nivel más interno haciendo uso de la librería phonegap como interfaz hacia dos de las funcionalidades requeridas: en una de ellas se retomaría un plugin phonegap ya existente (lo que garantizaría no perderse demasiado en los mecanismos de la comunicación) para centrarse en la implementación java del salvado del fichero devuelto por el web service a una ruta interna del dispositivo. En la otra funcionalidad se trataría de implementar también en java el lanzamiento de una petición (intent) a un plugin ya existente llamado barcodescanner, plugin capaz de escanear un código BIDI y devolver su información original.
Nada más comenzar con el trabajo de interfaz nos topamos con las dos primeras trabas. En principio, eventos sencillos como pulsar un botón no eran atendidos adecuadamente. Tras algunos rodeos llegamos a la conclusión (más como un resultado de una prueba y error que de la identificación correcta de la causa) de que el orden de la llamada a las librerías javascript seguía una secuencia a respetar: cualquier referencia a un plugin, como era el caso de phonegap, debería ir colocada entre la referencia al script de la librería jquery (p.e: jquery-1.6.3.min.js) y la referencia al script de la librería jquery-mobile (p.e: jquery.mobile-1.0rc2.js).
La otra traba inicial tuvo que ver con el arranque de la aplicación en el simulado. Con frecuencia (una frecuencia que dependía del estado de rendimiento del equipo de desarrollo) el inicio de la aplicación se quedaba en el limbo de los inicios. Tras bucear por el ancho google se resolvió el problema forzando un tiempo de espera en el método de instanciación de la clase principal (mediante la modificación de la propiedad loadUrlTimeoutValue de la clase padre DroidGap).
La llamada a los web service (que en principio no se presumía fuera un problema) se convirtió en problema al plantear una comunicación digamos más compleja hacia servicios .NET. Tras diversos avatares, entre restricciones de servidor web (un clásico servicio web sobre IIS 6 se había quedado demasiado obsoleto para estos nuevos tiempos) y de entorno de desarrollo (Visual Studio 2005 no respondía a la necesidad de incorporar el elemento Access-Control-Allow-Origin en la cabecera de la respuesta HTTP como requisito para salvar las restricciones de seguridad impuestas por el javascript dentro del dispositivo) tuvimos la fortuna de toparnos con el código fuente de una solución llamada ServiceStack. Se trataba de una solución (ServiceStack.Examples) que iba mucho más allá de nuestros propósitos pero en la que la unión de varios de sus proyectos (Host.WindowsService, Host.Console, ServiceStack.Examples.ServiceModel y ServiceInterface) implementaban un servicio windows como si fuera un servicio web (sin necesidad de pasar por ningún IIS) y ya de paso resolvía la manera de modificar esa cabecera del mensaje que tanto necesitábamos. La apuesta fue un acierto y una excusa para explorar otra forma de comunicar sistemas, puede que menos ortodoxas pero sí treméndamente prácticas.
La copia del fichero PDF devuelto en una ruta interna del dispositivo sólo tuvo por contratiempos el de encontrar un descodificador de Base64 (codificación de la cadena en la que viajaba la información del fichero) y el de localizar la identificación del tipo de fichero (application/pdf) necesaria para su lectura por cualquier otra aplicación del dispositivo dispuesta a visualizarlo.
La implementación del código que requería el uso del plugin barcodescanner supuso el escollo final y salvarlo pasaba por la unión adecuada de las piezas del rompecabezas llamado phonegap a las que se añadían las características propias de la comunicación en Android mediante entradas de tipo ativity que debían trasladarse al fichero de configuración de la aplicación (AndroidManifest.xml).
El resultado final fue una aplicación que evidenciaba que el potencial de actuar sobre un dispositivo móvil a través de servicios web y plugins phonegap se prestaba a futuros desarrollos dentro de Panel. Finalmente y a modo de ejemplo os dejamos constancia de los detalles de implementación de la llamada al plugin barcodescanner diseminados por el código fuente:
Función javascript que lanza el escaneo
function EscanearQR (){
var resultado = “”;
window.plugins.barcodeScanner.scan(
function(result) {
resultado = result.text;
…
}, function(error) {
alert(“Error en el escaner: ” + error);
});
}
Código javascript que permite el interfaz con phonegap
var BarcodeScanner = function() {
};
BarcodeScanner.prototype.scan = function(successCallback, errorCallback) {
if (errorCallback == null) { errorCallback = function() {}}
if (typeof errorCallback != “function”) {
console.log(“BarcodeScanner.scan failure: failure parameter not a function”);
return
}
if (typeof successCallback != “function”) {
console.log(“BarcodeScanner.scan failure: success callback parameter must be a function”);
return
}
PhoneGap.exec(successCallback, errorCallback, ‘BarcodeScanner’, ‘scan’, []);
};
Parte de código java que gestiona la llamada al objeto de tipo Intent y que forma parte del fichero BarcodeScanner.java proporcionado con el código fuente del plugin BarcodeScanner
public class BarcodeScanner extends Plugin {
…
public PluginResult execute(String action, JSONArray args, String callbackId) {
…
if (action.equals(“scan”)) {
Intent intentScan = new Intent(“com.phonegap.plugins.barcodescanner.SCAN”);
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
this.ctx.startActivityForResult((Plugin) this, intentScan, REQUEST_CODE);
}
…
}
…
}
Entrada de tipo activity que necesita el fichero AndroidManifest.xml para que la llamada originada en el objeto de tipo Intent se ponga a disposición del plugin BarcodeScanner
<application
…
<activity android:name=”com.google.zxing.client.android.CaptureActivity” android:screenOrientation=”landscape” android:configChanges=”orientation|keyboardHidden” android:theme=”@android:style/Theme.NoTitleBar.Fullscreen” android:windowSoftInputMode=”stateAlwaysHidden”>
<intent-filter>
<action android:name=”com.phonegap.plugins.barcodescanner.SCAN”/>
<category android:name=”android.intent.category.DEFAULT”/>
</intent-filter>
</activity>
…
</application>
Referencias:
Turoriales Phonegap (http://hiediutley.com/category/mobile-development/phonegap/)
Plugin BarcodeScanner (https://github.com/phonegap/phonegap-plugins/tree/master/iPhone/BarcodeScanner)
Service Stack (http://www.servicestack.net/)
ola juan carlos, gracias por la información q nos compartes en este post, podrias compartirnos el código fuente de esa aplicación generada porfavor?.. porque hay cosas que aún no entiendo y me seria mas fácil así.
Gracias