Apache Cordova

¿Qué es?

Es un conjunto de librerías que permite empaquetar aplicaciones HTML5 de manera que puedan ser usadas como apps para móviles. También nos permite acceder a servicios nativos del móvil a través de Javascript.

Server: irc.freenode.net
Channel: #phonegap

Apache Cordova

Permite compilar en la nube. De esta forma no será necesario instalar los SDK de los sistemas para los que queremos compilar. Por otra parte, para compilar una aplicación para IOS es necesario un ordenador MAC. Esto lo podremos salvar utilizando PhoneGap Build.
https://build.phonegap.com/

Una vez registrados, la opción
upload an archive or index.html file
Nos permite subir tanto un archivo .zip como un archivo .html

Luego podremos instalar la aplicación en el móvil escaneando su código de barras la aplicación de android barcode scanner o bien descargar el apk.

Consideraciones a tener en cuenta antes de subir la app a PhoneGap Build

Habrá que eliminar el fichero phonegap.js del proyecto. Hacemos esto porque phonegapbuild requiere una librería diferente por cada plataforma y poner nosotros una librería concreta ocasionaría problemas al ejecutar la aplicación.

Para cargar la librería que phonegapbuild colocará utilizaremos la siguiente línea:

	
<script src="phonegap.js"></script>
	

Notas:

En las últimas versiones de Phonegap Build la compilación para BlackBerry, Web OS y Symbian no funciona.

Ripple

Simulador de aplicaciones móviles. Sirve tanto para aplicaciones web para móviles como para phonegap.

Podemos instalarlo desde la chrome store.

Tras la instalación, aparecerá el logo de Ripple a la derecha de la url de direcciones del navegador.

Para probar nuestra aplicación basta con acceder a la url de la misma y habilitar ripple (en lo sucesivo quedará habilitado para esta url).

Ya que estamos trabajando con la última versión de phonegap, esta será la que habrá que establecer por defecto en Ripple.

Por seguridad chrome no permite que los plugins (Ripple) accedan al sistema de archivos local. Por tanto, para poder testear nuestra aplicación necesitaremos que esté subida a un servidor, aunque sea local.

Si no funcionase Ripple (al pulsar en enable no hace nada), podemos pulsar en preferencias → herramientas → extensiones → Allow access to file URLs

Instalación

Crear una nueva aplicación

$  sudo su 
$ cordova create my-app com.pablomonteserin.myapp #Es conveniente indicar un nombre de paquete o de lo contrario, cuando compilemos para subir a la tienda correspondiente se nos indicará que ese paquete ya estaba en uso
$ cd my-app
$ cordova platform add android

Compilar

Compilar sin firma

Debug

sudo cordova build android

Ejecutado desde la raíz de nuestro proyecto cordova, mueve el contenido de la carpeta www a las carpetas de las plataformas que hayamos añadido a nuestro proyecto.

Release

cordova build --release android
  • Necesitaremos tener la licencia que estamos usando del SDK de Android aceptada. Para aceptarlas todas:
    /[ruta-del-sdk-de-Android]/tools/bin/sdkmanager --licenses

Generar una firma (keystore)

Si estamos usando el Android Studio, podemos generar una firma o vincular nuestro proyecto a una firma ya existente desde el menú Build -> Generate Signed APK

Compilar con firma

cordova build --release android

Debemos crear un fichero en la raíz de nuestro proyecto Cordova:

build.json"android": {
    "debug": {
        "keystore": "..\mykeystore\CordovaDebug.keystore",
        "storePassword": "secretpassword",
        "alias": "CordovaDebug",
        "password" : "secretpassword",
        "keystoreType": ""
    },
    "release": {
        "keystore": "..\mykeystore\CordovaRelease.keystore",
        "storePassword": "",
        "alias": "CordovaRelease",
        "password" : "secretpassword",
        "keystoreType": ""
    }
}

Recomendaciones para compilar con Cordova

  • En linux y mac, lugar de ejecutar los comandos con 'sudo' delante, te recomiendo mucho loguearte directamente como super asministrador:
    sudo su
  • Debemos tener Java 8 instalado, no sirve una versión superior
  • Debemos tener las variables de entorno ANDROID_HOME y JAVA_HOME exportadas. Si ejecutamos este código desde la consola (con las rutas correctas) en linux y Mac las variables estarán exportadas hasta que cerremos la consola:
    En Linux:export ANDROID_HOME=/home/monty/Android/Sdk
    export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
    export JAVA_HOME=/usr/lib/jvm/java-8-oracle/
  • spawn EACCES: Este es un problema de permisos en mac y Linux. Lo solucionamos así:
    sudo chmod -R a+rwx carpeta-de-la-aplicacion/

Compilación con Android Studio

Abriremos la carpeta platforms/android desde el Android Studio y compilaremos desde aquí

Arrancar el emulador desde Cordova

Ejecutaremos el siguiente comando:

cordova emulate android

Consideraciones (lo siguiente, es más cómodo gestionarlo desde el propio Android Studio):

  • Tras ejecutar este comando, en las primeras trazas de la consola, se nos indica la versión de Android para la que estamos compilando. Debemos tener esa versión de Android añadida con el Android Virtual Device Manager.
  • Debemos tener arrancado ya el emulador.

config.xml

Versionado

Modificaré el atributo version del nodo widget del fichero config.xml situado en la raíz de nuestro proyecto cordova. Esto modificará automáticamente atributo opcional android-versionCode del mismo nodo, que es el que a su vez modificará el atributo android:versionCode del AndroidManifest.xml generado.

Aplicación horizontal y a pantalla completa

	<preference name="Orientation" value="landscape" />
	<preference name="Fullscreen" value="true" />
</widget>

Posibles errores al subir la aplicación a google play (https://play.google.com/apps/publish) utilizando el apk generado por phonegap build

You need to use a different package name because "com.phonegap.www" already exists in Google Play.
→ habrá que definir un paquete para la aplicación:

  • Si estamos usando phonegap build online: phonegapbuild page → settings → configuration
  • Si estamos usando cordova en nuestro equipo: config.xml en la raíz de nuestro proyecto

You uploaded an APK that was signed in debug mode. You need to sign your APK in release mode.
Habrá que firmar la aplicación.

Hola mundo - Phonegap

	
<doctype html>
<html>
	<head>
		<meta charset="UTF-8">
		<!-- la siguiente línea evita que los números de teléfono se conviertan en links clickables-->
		<meta name="format-detection" content="telephone-no">
		<!-- la siguiente línea evita que el usuario pueda hacer zoom sobre la aplicación, como ocurre con una página web, -->
		<meta name="viewport" content="user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
		<title>Mi aplicación</title>
		<link rel="stylesheet" href="css/estilos.css">
		<script src="cordova-2.5.0.js"></script>
		<script>
			document.addEventListener("deviceready", onDeviceReady, false);
			function onDeviceReady(){
				alert("hola mundo!");
			}
		</script>
	</head>
	<body>
		<div>Place holder</div>
	</body>
</html>
	
Ver ejemplo

Notificaciones (beep, vibrate alert)

	
navigator.notification.alert("Este es un mensaje");

// Beep three times
navigator.notification.beep(3);

// Vibrate for 2 seconds
navigator.notification.vibrate(2000);
	
Ver ejemplo.

Reproducción multimedia

		
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady(){
	//var reproductor = new Media("http://www.pablomonteserin.com/curso/javascript/phonegap_exercises/2_media/Hakuna.mp3", onSuccess, onError);

	//Para cargar archivos locales (en android) tendremos que indicar la ruta de la siguiente forma:
	var reproductor = new Media("/android_asset/www/Hakuna.mp3", onSuccess, onError);

	reproductor.play();
}
function onSuccess(){
	alert("reproduccionOK");
}

function onError(error){
	console.log(error.message);
}
		
	
Ver ejemplo.

Geolocalización


<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<link rel="stylesheet" type="text/css" href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css">
<script type="text/javascript">
/*
-PhoneGap Documentation: The Android 2.x simulators will not return a geolocation result unless the enableHighAccuracy option is set to true.
- Para que se ejecute deviceReady, será necesario ejecutar la aplicación en Ripple, en el emulador, o en móvil; no basta sólo con ejecutar en un navegador, aunque sea a través de localhost.
- Si ejecutamos en el emulador es probable que sea necesario mandar las coordenadas de nuestra ubicación para que funcione la geolocalización (En eclipse: Window → show view → Emulator Control Location Controls → Manual → Send)
*/
document.addEventListener("deviceReady", deviceReady, false);
function deviceReady(){
	navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoError, { enableHighAccuracy: true });
}

function onGeoSuccess(position){
	var lat = position.coords.latitude;
	var lon = position.coords.longitude;
	var currentPosition = new google.maps.LatLng(lat, lon);
	var mapoptions = {
		zoom : 12,
		center: currentPosition,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	var map = new google.maps.Map(document.getElementById("map"), mapoptions);
	var marker = new google.maps.Marker({
		position: currentPosition,
		map: map
	});
}
function onGeoError(error){
	if(error==1){
		alert("turn on geolocation services")
	}else{
		alert("geoError: " + error);
		alert("geoError: " + error.message);
	}
}
	
Geolocation

Uso de la cámara de fotos

		
document.addEventListener("deviceready", onDeviceReady, false);
var valores;

function onDeviceReady(){
	alert("inicia");
	capturador = navigator.device.capture;
}

function captura_audio(){
	alert("captura audio");
	capturador.captureAudio(capturaOK, capturaError);
}
function captura_video(){
	alert("captura video");
	capturador.captureVideo(capturaOK, capturaError);
        navigator.camera.getPicture(onSuccess, onFail, { quality: 50 }); 
}
function captura_imagen(){
	alert("captura imagen");
	capturador.captureImage(capturaOK, capturaError);

}
function capturaOK(archivos){
	alert("capturaOK")	
	for(var i =0; i<archivos.length; i++){
		var ruta = archivos[i].fullPath;
			alert(ruta)	
	}
}
function capturaError(){
	alert("error en la captura");
}
		
	
código qr aplicación phonegap Descargar APK.

Notas:

En el caso de Android, si un Activity es totalmente eclipsado por otro, será parado, almacenando su estado e información. Sin embargo, es posible que sea eliminado por el sistema cuando los requerimientos de memoria así lo estimen. Por tanto, al volver del capturador de multimedia es posible que la aplicación sea reiniciada, en lugar de ir al método capturaOK.

Lectura de ficheros

		
document.addEventListener("deviceready", function(){
	alert("deviceready");
	window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, archivosDisponibles, function(){
		alert("No esta disponible el sistema de archivos");
	});
}, false);

function archivosDisponibles(fileSystem){
//fileSystem.root nos da la carpeta de almacenamiento persistente asociada a nuesta aplicación, de donde podremos escribir y leer archivos
	alert(fileSystem.root.fullPath);
	fileSystem.root.getFile("readme.txt", null, archivoLeido, function(){
		alert("No esta disponible el sistema de archivos 2");
	});
}

function archivoLeido(archivo){
	alert("archivo leido");
	archivo.file(function(archivo){
		var reader = new FileReader();
		reader.onloadend = function(evento){
			alert(evento.target.result);
		}
		reader.readAsText(archivo);
	}, function(){
		alert("Error");
	});
}
		
	
Ver ejemplo.

Notas:

Probablemente tengamos que editar el contenido de un fichero readme.txt ubicado en la raíz del sistema de ficheros que vemos al conectar nuestro móvil al ordenador para tener una información que leer.

Uso de varios frameworks

Si estamos usando deviceReady y jQuery y jQueryMobile, es probable que queramos asegurarnos de que todo este cargado antes de hacer nuestras llamadas a los servicios del teléfono. Para ello, podemos usar el siguiente sistema:

/*DeviceReadyDeferred y jqmReadyDeferred son dos variables que utilizaremos como flags para controlar cuando el framework fue cargado.*/
var deviceReadyDeferred = $.Deferred();  
var jqmReadyDeferred = $.Deferred();	
document.addEventListener("deviceReady", deviceReady, false);
function deviceReady(){
	deviceReadyDeferred.resolve(); // el framework deviceReady fue cargado
}
/*Iniciaremos jQueryMobile cuando la section cuya id es firstpage esté cargada. Por tanto, deberé tener una página con la estructura propia de jQueryMobile*/
$(document).on("pageinit", "#firstpage",function(evt){
	$(document).bind("pageshow", "#firstpage",function(e, data){
		jqmReadyDeferred.resolve(); // el framework jquery mobile fue cargado	
	});
});

$.when(deviceReadyDeferred, jqmReadyDeferred).then(doWhenBothFrameworksLoaded);
function doWhenBothFrameworksLoaded() {
...
Ver ejemplo

Envío de información al servidor

Este sería el código que recogería la información enviada.

<?php
	$valor = $_REQUEST['valor'];
	echo $valor;

	$conexion = mysqli_connect('localhost', 'root', 'pp', 'test')
	or die("hubo un error al conectar con la base de datos");

	mysqli_set_charset($conexion, "utf8");
	$sql = "INSERT INTO usuario(name) VALUES('$valor')";
	mysqli_query($conexion, $sql)or die($sql);
?>

Consejos para mejorar la aplicación

No esperar a que la página esté totalmente cargada para acceder a la siguiente página

En lugar de solicitar la página y esperar que esté lista para cargarla, lo que haremos será cargar la siguiente página (para que el usuario se dé cuenta de que ha ocurrido algo), mostrar una ruedita de loading, y finalmente pintar la página cuando ya la tengamos.

Cachea tus datos

Cachea tus datos (ya sean estáticos o dinámicos) (en localStorage o en la base de datos).

displayUI();
//Primero cargo lo que hay en la cache y luego lo que viene del server.
cache.loadData().done()(function(data){
	ShowData();
});
server.loadData().done()(function(data){
	ShowData();
});

Haz las animaciones de una página a otra en CSS en lugar de Javascript

Esto incrementará la velocidad de la aplicación.

Cuando queramos ejecutar la animación, añadiremos al elemento un class que se encargará de hacerla.

Haz las animaciones CSS usando aceleración por hardware (llamando a translate3d(,,))

.page{
	Position:absolute; top:0; left:0; width:100%; height:100%; 
	transform:translate3d(0,0,0);
}
.page.left{
	transform: translate3d(-100%, 0, 0)
}
.page.center{
	transform:translate3d(0,0,0)
}
.page.right{
	transform: translate3d(100%, 0, 0);
}
.page.transition{
	transition-duration: .25s;
}

Podemos usar una librería para automatizar este proceso:
https://github.com/ccoenraets/PageSlider

Evitar el delay de 300ms de los clicks

Cuando hacemos click sobre un item, el sistema espera 300 ms para comprobar si se trata de un click o de un doble click.

Para evitarlo, podemos sustituir todos los clicks por touch events usando:
https://github.com/ftlabs/fastclick

Otros

  • Usar sprites para la carga de fotos (CSS sprite sheets).
  • Limitar el uso de sprites y shadows.
  • Usar un framework como jQueryMobile puede relentizar bastante la aplicación. Debemos pensar si realmente lo necesitamos; probablemente haya soluciones más livianas. Además, a menudo usar jQueryMobile implica un gran trabajo de reestilizado.
icono de mandar un mail¡Contacta conmigo!
contacta conmigoPablo Monteserín

¡Hola! ¿En qué puedo ayudarte?