-
Notifications
You must be signed in to change notification settings - Fork 1
Guardando fotos
Guardar las fotos que sacamos desde la aplicacion de Galeria utilizando los metodos anteriores. Trabajen sobre prueba12
.
El archivo index.html
queda asi (los botones que usamos para probar los archivos de texto no molestan, asi que los dejamos). No hay desafios aca.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<link rel="stylesheet" type="text/css" href="css/jquery.mobile-1.4.5.min.css" />
<script type="text/javascript" src="js/jquery.min.js"></script>
<title>Galeria</title>
<style>
.picThumb {
max-width: 20%;
}
img.foto {
max-width: 100%;
}
</style>
</head>
<body>
<div data-role="page" id="home">
<div data-role="header">
<h1>Galeria de fotos</h1>
</div>
<div data-role="content" class="galeria">
<p class="fileContent"></p>
<a href="#" id="grabarArchivo" class="ui-btn ui-btn-b">Grabar</a>
<a href="#" id="leerArchivo" class="ui-btn ui-btn-b">Leer</a>
</div>
<div data-role="footer" data-position="fixed">
<a href="#" id="sacarFoto" style="display:block" class="ui-btn ui-btn-b ui-icon-camera ui-btn-icon-top">Foto!</a>
</div>
</div>
<div data-role="page" id="fotoView">
<div data-role="header">
<a href="#" class="ui-btn" data-rel="back">Volver</a>
<h1>Foto</h1>
</div>
<div data-role="content">
<img class="foto" src="" />
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript" src="js/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
En el archivo js/index.js
tenemos la inicializacion con Deferred's (pueden ir copiando y pegando los bloques de codigo, deberian terminar armando todo el archivo):
var deviceReady = $.Deferred();
var jqmReady = $.Deferred();
var documentReady = $.Deferred();
$(document).on("ready", function() {
documentReady.resolve();
});
$(document).on("deviceready", function() {
deviceReady.resolve();
});
$(document).on("mobileinit", function () {
jqmReady.resolve();
});
//cuando los 3 se resuelvan, ejecutar init()
$.when(deviceReady, jqmReady, documentReady).then(init);
La funcion init()
inicializa la configuracion de jQuery Mobile, la funcionalidad de la camara y el fileApi que hicimos:
function init() {
// Configuracion de JQM para phonegap
$.mobile.allowCrossDomainPages = true;
$.support.cors = true;
$.mobile.buttonMarkup.hoverDelay = 0;
$.mobile.pushStateEnabled = false;
$.mobile.defaultPageTransition = "none";
//inicializamos la funcion de la camara y el fileApi
camara.initialize();
fileApi.initialize();
}
NOTA: el objeto camara
y fileApi
pueden ubicarse en cualquier orden en el codigo mientras mantengan la integridad del objeto (estos bloques de codigo son demostrativos, no van en el archivo index.js
):
- el objeto se declara como una variable:
var camara = {}
- el objeto puede tener metodos dentro, separados por coma:
var camara = {
metodo1: function() { alert('Hola'); },
metodo2: function() { alert('Chau'); }
}
- el ultimo metodo no debe llevar una coma cuando se cierra
- los metodos son como variables dentro del objeto, pero se asignan con dos puntos (:), no con el signo igual (=)
var camara = {
metodo1: function() { alert('metodo correcto'); },
metodo2 = function() { alert('metodo erroneo, eleva un error y detiente la ejecucion') }
//^^^^^^^^^^^^^^^^^^^^
}
FIN DE NOTA
Luego tenemos este objeto que nos sirve para inicializar la camara de manera ordenada y reusable (voy a partir el objeto en cada metodo, pero pueden seguir copiando y pegando cada bloque en orden y deberia quedar bien):
Declaracion del objeto y metodo para inicializacion
var camara = {
initialize: function() {
if(localStorage.galeria) {
$.each(JSON.parse(localStorage.galeria), function(i,e){
camara.addPictureToGallery(e.path);
});
}else{
localStorage.galeria = JSON.stringify([]);
}
$('#sacarFoto').on('click', camara.sacarFoto);
},
Al inicializar, verificamos si existe localStorage.galeria
. De ser asi lo parseamos y asumimos que es un array. Iteramos sobre este array y (este es uno de los cambios) por cada item del array sabemos/suponemos que tendremos un objeto {name: "nombreDelArchivo", path: "rutaAlArchivo"}
. Sabiendo esto, llamamos a camara.addPictureToGallery(e.path)
(donde e
es el valor del item iterado).
Si localStorage.galeria
no existe, le asignamos un array vacio convertido en texto con el metodo JSON.stringify
.
Por ultimo, asignamos al boton $('#sacarFoto')
la funcion camara.sacarFoto
cuando surja el evento click
.
sacarFoto
sacarFoto: function() {
var options = {
destinationType : Camera.DestinationType.DATA_URL,
sourceType : Camera.PictureSourceType.CAMERA,
saveToPhotoAlbum: true
};
navigator.camera.getPicture(camara.onDataUrlSuccess, camara.onError, options);
},
El metodo sacarFoto
llama a navigator.camera.getPicture
con un set simple de opciones. Como callback pasa camara.onDataUrlSuccess
onDataUrlSuccess
onDataUrlSuccess: function(imageData) {
//en cuanto empieza esta funcion
//mostramos un "loading" o spinner
$.mobile.loading('show');
//traigo la galeria del localStorage y la parseo
var fotos = JSON.parse(localStorage.galeria);
//genero un id para la imagen, sirve de filename tambien
var id = 'img_' + parseInt(Math.random() * 1000000);
//escribo la imagen y a la vuelta agrego el id:path al storage
fileApi.writeJpeg(id + '.jpg', imageData, function(jpegPath){
//con el id y el path, hago un objeto y lo agregamos al array
fotos.push({name:id, path: jpegPath});
//por ultimo convertimos el array en string
//y lo volvemos a almacenar en localStorage
localStorage.galeria = JSON.stringify(fotos);
//agregamos la imagen a la galeria (HTML)
camara.addPictureToGallery(jpegPath);
});
},
El metodo onDataUrlSuccess
es el callback asignado para cuando sacamos la foto. Recibe como parametro el archivo JPG codificado en base64.
Luego actualiza el registro de fotos en localStorage.galeria, guarda la foto y la agrega a la galeria (HTML):
- Parsea el valor de localStorage.galeria en un array
fotos
- Genera un
id
img_ con un numero al azar concatenado - Llama a
fileApi.writeJpeg
pasandole- El
id
generado (ej: img_823391) y la extension ".jpg" como nombre de archivo - El archivo codificado en base64 como contenido
- Una funcion para el callback que
- Recibe el path donde queda guardado el archivo
- Agrega un objeto al array fotos con:
- name:
id
generado - path: la ruta recibida por la funcion
- name:
- Vuelve a convertir el array fotos con
JSON.stringify
y lo almacena en localStorage.galeria - Llama a
camara.addPictureToGallery
pasandole el path donde queda guardado el archivo
- El
addPictureToGallery y onError
addPictureToGallery: function(image) {
var pic = $('<img />')
.addClass('picThumb')
.attr('src', image)
.appendTo('.galeria');
pic.click(function(e){
$('#fotoView img').first().attr('src',image);
$('body').pagecontainer('change','#fotoView');
});
$.mobile.loading('hide');
},
onError: function(message){
console.log(arguments);
alert('Error: ' + message);
}
}
addPictureToGallery
genera un tag/elemento HTML <img />
, le agrega la clase picThumb
, le agrega el attributo src
con el path a la imagen (recibida como parametro en la funcion) y agrega el elemento al elemento que tiene clase .galeria
.
A este mismo <img />
le asigna una funcion al evento click
:
- Buscar un
img
dentro de#fotoView
(la pagina que usamos para ver las fotos) - Como puede devolver mas de uno, pedimos el primero con
.first()
- A ese
img
le cambia el valor del attributosrc
por el del path de la foto (insertando efectivamente la imagen en la pagina#fotoView
) - Navega a la pagina
#fotoView
(por codigo)
Y por ultimo esconde el loading/spinner que mostramos cuando iniciamos el proceso, alla por onDataUrlSuccess
.
El objeto que generamos para ayudarnos con la lectura y grabacion de archivos. Este lo tienen mas fresquito, asi que no voy tan detallado.
var fileApi = {
initialize: function(){
window.resolveLocalFileSystemURL(cordova.file.externalDataDirectory, fileApi.onDir, fileApi.onError);
$('#grabarArchivo').click(function(){
fileApi.writeTextFile('test.txt', 'texto de prueba', function(data){
alert('Escribi '+data+' en test.txt');
});
});
$('#leerArchivo').click(function(){
fileApi.readTextFile('test.txt', function(data){
$('.fileContent').text('Contenido del archivo: '+data);
});
});
},
onDir: function(directoryEntry) {
fileApi.dir = directoryEntry;
},
onError: function(err) {
alert(err.code);
},
writeTextFile: function(file, content, callback) {
var onFile = function(fileEntry) {
fileEntry.createWriter(function(fileWriter){
fileWriter.write(content);
callback && callback(content);
}, fileApi.onError);
}
fileApi.dir.getFile(file, {create: true}, onFile, fileApi.onError);
},
readTextFile: function(file, callback) {
var onFile = function(fileEntry) {
//convierte el fileEntry en un fileObject
fileEntry.file(function(fileObject){
var reader = new FileReader();
reader.onloadend = function(){
callback && callback(this.result);
}
reader.readAsText(fileObject);
});
}
fileApi.dir.getFile(file, {create:false}, onFile, fileApi.onError);
},
writeJpeg: function(fileName, base64content, callback) {
var onFile = function(fileEntry) {
fileEntry.createWriter(function(fileWriter){
var blob = b64toBlob(base64content,'image/jpeg');
fileWriter.onwriteend = function(){
console.log(this);
callback && callback(fileEntry.nativeURL);
}
fileWriter.write(blob);
}, fileApi.onError);
}
fileApi.dir.getFile(fileName, {create: true}, onFile, fileApi.onError);
}
}
b64toBlob
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
Esta es la funcion que convierte el archivo codificado en base64 a Blob. Deberian tenerla al menos al final del archivo
Entender el codigo e implementarlo en la aplicacion. Copien y peguen, drive safe. Traten de ver las imagenes/archivos de texto a traves del explorador de windows (como para verificar).
Para entretenerse:
https://github.com/AllThingsSmitty/jquery-your-mom-should-know
- Repaso y conceptos basicos
- Ejercicios para repasar
- Problematica de desarrollo mobile
- Phonegap
- Requerimientos e instalacion
- Phonegap CLI
- [Primeros ejercicios con Phonegap](Ejercicio Phonegap)
- Plugins
- Hello world
- Estructura de un proyecto Phonegap
- Plugin Device
- Debugging
- Incorporando un framework CSS
- Plugin Vibration
- Sumando partes
- Plugin Battery Status
- Un poco de jQuery
- jQuery Mobile
- Navegacion
- Plugin Dashboard
- Revision de Plugin Dashboard
- Paginas extra
- ToDo
- Persistencia
- ToDo Persistente
- Plugin Camera
- Opciones de camara
- App Mi Galeria
- Revision de Galeria
- Plugin File
- Escribir y leer texto
- Escribir archivos binarios
- Guardando fotos
- Debugger: weinre
- Inspeccionar con weinre
- Phonegap Developer App
- Refactor de apps
- Plugin Media
- Pruebas con audio
- Control y monitoreo de audio
- Encapsular play/pausa
- Hacks
- Formato de tiempos
- Grabar y reproducir
- Proyecto Integrador
- Revision de interfaz
- Interfaz basica
- Entrevista: modelo
- Funcionalidad: revision
- Seleccion de framework
- iRec: navegacion
- iRec: fileApi
- iRec: guias
- iRec: entrevistas
- iRec: recordApi
- iRec: helpers
- iRec: mediaApi
- iRec: inicializar paginas
- iRec: refactor
- iRec: mediaApi refactor
- iRec: recordApi refactor
- iRec: helpers
- iRec: guias.js
- iRec: entrevistas.js
- iRec: crear guias
- iRec: re-inicializando
- iRec: re-ordenando
- iRec: pendientes
- iRec: reporte ux
- iRec:revision: volver
- iRec:entrevista: volver
- iRec:revision: cambios
- iRec:revision: stop
- iRec:revision: Rew 10"
- iRec:revision: tag Go
- iRec: limpieza
- Firma de apps
- Android
- Generacion de key
- Firma de apk
- Alineacion de zip
- Firmar con Phonegap
- iOS
- Detalles finales
- Config.xml
- Iconos
- Splash