PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 60
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4. Desarrollo de la implementación
Una vez introducidas todas las partes involucradas en este proyecto, a
continuación se detalla la implementación llevada a cabo en Android. Para ello
es preciso tener claro el contexto en el que se encuentra la aplicación. Algunas
de las decisiones tomadas se han tomado con el motivo de simplificar el
resultado final y pueden no ser las más óptimas.
4.1. Introducción
Los objetivos que se quieren acometer son principalmente dos:
Búsqueda de clientes y compañías
Creación de nuevos clientes y compañías
La interfaz que nos ofrece el servicio web escogido viene documentada
en un fichero PHP. La ruta es la misma que el punto de entrada a dicho servicio,
el cual es:
<ruta_hasta_el_portal_crm/service/v4/rest.php
Existen varias versiones del servicio, la última disponible cuando se
empezó la implementación era la versión 4. En cada sección del presente punto
se expondrá los métodos utilizados del servicio.
Para la utilización de estos métodos, se ha desarrollado la clase
sugar.java. Esta clase es el corazón principal de la aplicación y prácticamente
esta presente en toda la aplicación.
En cuanto al funcionamiento de la aplicación se basa en las diferentes
Activities.
1. Login.java: autentica al usuario en el portal.
2. Menú.java: presenta las opciones clientes o compañías.
3. Clientes.java: búsqueda y creación de clientes.
4. Comp.java: búsqueda y creación de compañías.
5. Ficha.java: muestra detalles de un cliente o compañía.
En el siguiente diagrama de flujo puede verse claramente el
funcionamiento de la aplicación:
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 61
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
Figura 29: Diagrama de flujo
En el siguiente punto se detalla la clase sugar así como los métodos
utilizados de la API REST. A continuación se mostrarán las diferentes clases que
componen la interfaz gráfica y finalmente se debatirán posibles mejores y
ampliaciones que se pueden realizar.
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 62
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.2. Interconexión servicio web y aplicación. Clase sugar.java
La clase sugar.java contiene todos los métodos que implementan la
conexión al servicio web. En los siguientes puntos se exponen los métodos de
los cuales se compone y el servicio web utilizado.
4.2.1. Constructor
El constructor de la clase pide como parámetros de entrada la URL del
portal SugarCRM y la ruta al punto de entrada REST. En el caso de este proyecto,
al ejecutar WampServer para el servidor, es necesario utilizar la dirección
localhost, la cual es traducida en el emulador Android por la IP 10.0.2.2. La ruta
hasta el punto de entrada es la ruta por defecto.
/* Cuidado con la dirección IP, 10.0.2.2 es localhost en android */ sugar pfc = new sugar("http://10.0.2.2/","sugar/service/v4/rest.php");
Estos parámetros son almacenados en sendas variables String en el
objeto sugar creado.
4.2.2. Objetos JSON
Como ya se explicó en el punto dedicado a REST, cada recurso tiene su
propia URI. En este caso, la URI a construir tiene 3 partes:
La primera parte ya ha sido explicada anteriormente, la segunda es como
sigue:
?method=login&input_type=json&response_type=json&rest_data=
Method: el método a usar
Input_type: formato de los parámetros de entrada (ej: json, xml)
Response_type: formato de la respuesta (ej: json, xml)
<ruta_hasta_el_punto_de_entrada><método a usar y tipo IN/OUT><parámetros>
Figura 30: URI a utilizar
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 63
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
La tercera parte son los parámetros que necesite el método utilizado y su
formato depende de lo que se haya especificado en la parte de input_type. El
formato utilizado durante todo el presente proyecto es JSON debido a que
generalmente se asocia REST con JSON (al igual que SOAP con XML, aunque esto
no tiene por qué cumplirse).
Los objetos JSON se componen de dos valores: un String que indica el
nombre del atributo y value que es el valor del atributo. Value puede ser
cualquier tipo de dato, otro objeto JSON o un array de objetos JSON. Para
indicar un array se utilizan corchetes.
Figura 31: Estructura de un objeto JSON
A continuación se presenta un ejemplo de un objeto JSON, en este caso,
el atributo “users” contiene un array de objetos, que a su vez, contienen más
atributos
Figura 32: Ejemplo JSON
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 64
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
En el presente proyecto, todos los parámetros son construidos utilizando
arrays porque la librería oficial de Android para JSONObject impide indicar el
orden de los atributos (en cambio JSONArray sí). Realmente, la definición de
JSON indica que el orden de los atributos no es relevante, pero SugarCRM
padece de un bug por el cuál los nombres de los atributos son ignorados y
simplemente se fija en el orden de estos.
En el siguiente enlace se puede comprobar el estado de dicho bug:
http://www.sugarcrm.com/support/bugs.html#issue_47259
4.2.3. Método Connect()
El método connect() es utilizado por todos los demás métodos pues se
encarga de construir la URI necesaria y utilizar HttpPost para realizar la petición.
Como parámetros de entrada son necesarios el método de la API REST así como
un objeto JSONArray con los parámetros.
Una vez concluida la petición, la respuesta es siempre un objeto JSON, el
cual es devuelto por el método.
Las variables web y ruta son inicializadas por el constructor.
try { URI website = new URI(web+ruta); HttpPost request = new HttpPost(website.toString()+method+URLEncoder.encode(array.toString())); HttpResponse response = client.execute(request); HttpEntity e = response.getEntity(); jsondata = new JSONObject(EntityUtils.toString(e)); } catch (Exception e) { // TODO Auto-generated catch block e.getMessage();
}
Figura 33: Connect()
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 65
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.2.4. Método login()
El método de la API REST utilizado en este caso es “login” y para
autenticar al usuario en el servicio web es necesario enviar el par
usuario/contraseña, siendo este par los parámetros de entrada al método. A
modo de ejemplo, se expone la documentación disponible:
La contraseña debe ser codificada utilizando el algoritmo MD5 para lo
cual se utiliza el siguiente código, donde pass es un String con la contraseña en
texto plano:
md5 = MessageDigest.getInstance("MD5"); md5.update(pass.getBytes(),0,pass.length()); object.put("password", new BigInteger(1,md5.digest()).toString(16));
El portal intentará autenticar al usuario y si tiene éxito creará un id de
sesión único (el cual será almacenado en la carpeta /tmp/ de apache). Este id
será devuelto a la aplicación y será necesario para cualquier petición posterior.
Este id es almacenado en el objeto sugar en la variable id mediante el método
SetId() y puede ser utilizado con el método GetId(). Generalmente, el id de
usuario suele ser el primer parámetro del resto de métodos.
Figura 34: Ejemplo documentación API REST
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 66
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.2.5. Método GetRecords()
Método para hacer búsquedas de clientes o compañías en la base de
datos. El método de la API utilizado es “get_entry_list” que incluye como
parámetro una sentencia SQL para aplicar directamente a la base de datos
MySQL. Para simplificar el funcionamiento, se han limitado los parámetros de
búsqueda a dos: nombre completo y dirección de correo electrónico. Esto es
debido a la alta complejidad que requeriría implementar todos los parámetros
de los que dispone un cliente (los datos están distribuidos en varias tablas).
Se utiliza el mismo método para clientes y compañías debido a que
supone un mínimo cambio en los parámetros.
El campo fields indica los campos que se requieren de la base de datos.
Para ello se utiliza el método auxiliar putfields(<módulo>), siendo módulo
“contacts” o “accounts” en función del registro a buscar.
4.2.5.1. Clientes
Para hacer una búsqueda de clientes es necesario que el segundo
parámetro del array sea “contacts” y la petición SQL sea tal que así:
,"contacts.id in ( select bean_id from email_addr_bean_rel inner join
email_addresses where email_addr_bean_rel.email_address_id =
email_addresses.id and email_addr_bean_rel.deleted = 0 and
email_addresses.deleted = 0 and bean_module = 'Contacts' and
email_addresses.email_address like '%"+mail+"%') AND (contacts.first_name
like '%"+name+"%' OR contacts.last_name like '%"+name+"%')"
La sentencia SQL hace un join de las tablas email_addr_bean y
emaild_addresses debido a que en una están almacenadas las beans o
identificadores mientras que en la otra se almacenan las direcciones
array.put(1,Character.toUpperCase(modulo.charAt(0))+modulo.substring(1)); if(modulo=="contacts") { array.put(2,”<Petición_SQL_Clientes>”); array.put(3,"first_name"); } else { array.put(2,”<Petición_SQL_Compañías>”); array.put(3,"name");
array.put(5,fields); }
Figura 35: GetRecords()
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 67
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
propiamente dichas. Hay dos partes diferenciadas, la primera relacionada con
las direcciones email (puede haber más de una) y la segunda con el nombre
completo del cliente.
4.2.5.2. Compañías
El caso de las compañías es similar al de los clientes siendo la diferencia
más notable que el módulo es “accounts”. La sentencia SQL queda como sigue
"accounts.id in ( select bean_id from email_addr_bean_rel inner join
email_addresses where email_addr_bean_rel.email_address_id =
email_addresses.id and email_addr_bean_rel.deleted = 0 and
email_addresses.deleted = 0 and bean_module = 'Accounts' and
email_addresses.email_address like '%"+mail+"%') AND accounts.name like
'%"+name+"%'"
4.2.6. Método SetNewRecord()
Este método es utilizado para crear nuevos registros en el portal web,
utilizando “set_entry” en la API. De igual manera que el método anterior, este
se usa indistintamente para clientes o compañías utilizando una clausula if para
determinar que objeto nuevo ha de crearse.
Apoyándose en las clases contacts.java y accounts.java (explicadas en el
siguiente punto), consigue crear el registro y mostrarlo por pantalla si el usuario
lo requiere.
4.2.7. Clases Contacts.java y Accounts.java
Las clases contacts.java y accounts.java representan un cliente y una
compañía respectivamente. Estas clases disponen de variables privadas con los
parámetros escogidos (se ha reducido el número de parámetros por simplicidad
con respecto a los originales) así como métodos específicos para acceder a estas
variables. Cabe destacar los métodos para generar la dirección de envío o
facturación completa.
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 68
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
El constructor está vacío, encargándose de ello el método
CreateContact() y CreateAccount(). Ambos métodos reciben como parámetro un
JSONObject obtenido directamente como respuesta de una petición al portal
web. La estructura de este JSONObject es como sigue:
Se trata de una estructura anidada, primeramente se compone de 4
atributos: result_count, total_count y next_offset son valores numéricos
mientras que el atributo entry_list contiene un array, cuyo número de objetos
coincide con el atributo result_count. Dentro de uno de estos objetos, se
encuentran 3 atributos más, id, module_name y name_value_list. Dentro del
último atributo se encuentran los valores de un registro codificados de la
siguiente manera:
“<nombre_atributo>”:{“name”:”<nombre_atributo>”,”value”:<valor>”}
Para facilitar la extracción de los valores, se utiliza el método getvalue(),
que devuelve directamente el campo identificado como “value” como un String.
Resumiendo, el método CreateContact/Account se utiliza para crear un
registro obtenido directamente desde el portal. En el caso inverso, en la
creación de un nuevo registro en el portal, es necesario codificar de manera
similar y para eso existe el método NewRecord().
Figura 37: create() y newrecord()
{“result_count”:”<valor_numérico>”, “total_count”:”<valor_numérico>”,
“next_offset”:”<valor_numérico>”,”entry_list”:[{“id”: “<id>”,”module_name”:”<nombre_modulo>”,”name_value_list”:{“assigned_user_name”:{“name”:”assigned_user_name”,”value”:”<valor>”}, …
Figura 36: Estructura de un objeto JSON de respuesta del portal
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 69
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.3. Clases Activities. Interfaz gráfica
Este punto está dedicado a las principales activities de la aplicación
Android. Como ya se ha explicado, una clase Activity construye la interfaz,
coloca los elementos visuales y reacciona a los estímulos del usuario. A
continuación se exponen todas las activities que componen esta aplicación, así
como una captura de pantalla para mostrar la interfaz de usuario.
4.3.1. Login.java
La pantalla de login da al usuario dos cuadros de textos para que
introduzca el par nombre de usuario y contraseña. Esto se realiza mediante el
elemento visual EditText. Primeramente, la Activity lee el fichero xml que se le
ha indicado mediante la sentencia setContentView() y lo muestra por pantalla.
Cada elemento visual tiene su propio id, el cual es almacenado en el
objeto R y así puede ser referenciado.
Una vez el cliente ha escrito usuario y contraseña, al pulsar en el
botón continuar se lanza un evento en el cual se crea un objeto de clase sugar y
se intenta hacer login. Si hay éxito, se procede al siguiente Activity pero en caso
contrario no se muestra ningún mensaje por pantalla (aunque sería fácil de
implementar mediante una notificación Toast).
setContentView(R.layout.login); ib = (Button) findViewById(R.id.iblogin); textuser = (EditText) findViewById(R.id.tuser); textpass = (EditText) findViewById(R.id.tpass);
<EditText android:id="@+id/tuser" android:layout_width="fill_parent" android:layout_height="wrap_content" android:ems="10" android:hint="@string/user" android:inputType="textPersonName" >
Figura 38: login.java
Figura 39: ejemplo EditText
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 70
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.3.2. Menu.java
La pantalla menú contiene 4 elementos visuales, de los cuales sólo son
funcionales dos: clientes y compañías. Mediante un selector (implementado con
una clausula switch) y apoyándose en un único listener permite iniciar la activity
correspondiente a la elección del usuario
Figura 41: Pantalla de login Figura 40: Diagrama login
Figura 43: Pantalla menu
Figura 42: Diagrama menu
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 71
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
La manera de realizar este procedimiento es implementando la clase
View.OnClickListener de manera que se puede escribir un único evento OnClick()
que será lanzado se pulse el botón que se pulse. Como argumento recibirá el
objeto View del recurso pulsado y así usarlo en la clausula switch.
4.3.3. Clients.java y Comp.java
Ambas clases son idénticas en cuanto a la forma y prácticamente
idénticas en cuanto a contenido. La diferencia es obvia, una es para clientes y
otra para compañías. Se ha optado por utilizar dos clases distintas por facilidad,
simplicidad y claridad. El simple hecho de que los clientes y compañías no
compartan atributos hace que si se hubiera optado por una única clase fuese
demasiado engorrosa de manejar.
La pantalla principal muestra una simple elección al usuario: buscar o
crear un registro. Ambas opciones crean dinámicamente la siguiente vista, que
consiste en un número variable de cuadros EditText para introducir parámetros.
En el caso de búsqueda (y como ya se vio en la clase sugar.java) solamente se
permiten las búsquedas por nombre completo y e-mail. Por otra parte, la
creación admite un número inferior a los proporcionados directamente por la
página, pero suficientes para identificar un registro.
Se ha realizado de manera dinámica por el simple hecho de poder
mostrar un número ilimitado de resultados en una búsqueda. Además del
public void onClick(View v) { String opcion = "error"; switch(v.getId()){ case R.id.bcontacts: opcion="clients"; break; case R.id.baccounts: opcion="comp"; break; case 0: break;
Figura 44: Clausula switch en menu.java
public class menu extends Activity implements View.OnClickListener{
Figura 45: Implementando View.OnClickListener
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 72
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
ahorro consecuente de tener que crear clases adicionales con su
correspondiente fichero xml.
En cualquier caso, al realizar una búsqueda o crear un nuevo registro, se
tiene la posibilidad de ver en detalle un registro. Para ello sólo hay que pulsar en
el propio registro y automáticamente se lanzará la pantalla ficha.java
4.3.4. Ficha.java
La última Activity de la aplicación se dedica a mostrar por pantalla los
atributos de un cliente o compañía. En este caso, ambos casos comparten una
única clase siendo solamente necesario una clausula if. La manera elegida para
public void onClick(View arg0) { // TODO Auto-generated method stub tl.removeAllViews(); final EditText nombre = new EditText(comp.this); nombre.setLayoutParams(new LayoutParams( LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT)); nombre.setHint("Nombre"); tl.addView(nombre);
Figura 46: Creación dinámica de una vista
Figura 48: Pantalla compartida clientes y compañías
Figura 47: Diagrama clientes y compañías
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 73
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
mostrar los detalles es a través de un ListView. Se trata de una lista desplazable
que son insertados automáticamente usando un Adapter que los saca de un
array.
El método createlist() crea un array con los atributos correspondientes
(en función de si se trata de un contact o un account) para que luego el
ArrayAdapter los coloque en el ListView. El resultado puede ser observado en
las capturas, no se incluye un diagrama puesto que no requiere ninguna
interacción por parte del usuario.
lv = (ListView) findViewById(R.id.flista); lista = createlist(this.getIntent().getExtras().get("record")); ArrayAdapter<String> arrayadapter =
New ArrayAdapter<String> (this,android.R.layout.simple_list_item_1, lista);
lv.setAdapter(arrayadapter);
Figura 49: Creando un ListView
Figura 50: Mostrando un cliente Figura 51: Mostrando una compañía
PROYECTO FIN DE CARRERA GUILLERMO GUERRERO GONZÁLEZ 74
APLICACIÓN ANDROID PARA ACCESO A SOLUCIÓN CRM
4.4. Detalles de la implementación
Existe multitud de obras que relatan cómo debe ser el acceso a un
servicio web utilizando REST buscando lo que se denomina los 4 principios REST:
Utiliza los métodos HTTP de manera explícita.
No mantiene estado.
Expone URIs con forma de directorios.
Transfiere XML, JavaScript Object Notation (JSON), o ambos
En este proyecto se ha intentado cumplir con todos estos principios
siendo no posible en el caso de las URIs y de los métodos HTTP. El motivo
principal es que el propio portal SugarCRM no cumple con estos 2 principios.
El soporte para objetos JSON son parte de Java desde la JSR-311 (Java
Specification Request), añadiendo las clases JSONObject y JSONArray usadas en
este proyecto. Existen librerías para un mejor soporte de este tipo de objetos
(por ejemplo GSON de Google) que no fueron necesarias.
Por otra parte, la guía de desarrollo para servicios web de SugarCRM no
contiene demasiada información (se centra en el lenguaje PHP) por lo que la
implementación realizada ha sido creada a partir de cero con ayuda de algunos
ejemplos encontrados en foros de discusión.
Top Related