• Cómo migrar una app de Google Play a Amazon App Store

    Para migrar en Amazon App Store una app que ya ha sido instalada en en Google, es suficiente con muy pocos cambios.

    Preparacion de la APP


    Version Name: en tu AndroidManifest.xml usa el atributo android:versionName para definir el nombre que será mostrado a los usurios.
    Version Number: android:versionCode Igual que en Google Play
    Device filtering: En tu AndroidManifest.xml especificar los dispositivos en los que se vara la apliciacion. Este atributo es obligatorio en Amazon

    Por ejemplo:

    <supports-screens
    android:smallScreens="false"
    android:normalScreens="true"
    android:largeScreens="true"
    android:xlargeScreens="false"
    android:requiresSmallestWidthDp="335"/>

    y recomendable en Google Play. Lo suyo es dejarlo tambien en Google, con lo que en archivo APK prácticamente solo hay que hacer un cambio: el archivo APK debe compilarse sin claves para firmar.

    El proceso de subir el archivo se hace en la consola de desarrolladores. Es muy intuitivo y parecido al de Google Play. El título de la app que se rellena en la ficha solo será para uso de la consola, pero es necesario que no sea demasiado común para no confundirlo con otras apps, según me dijeron en Amazon al rechazarme una app durante un test previo a la publicación.

    En resumen:

    1. Diferencias en librerías

      Prácticamente funcionan todas la librerías, con algunas salvedades relacionadas con los recursos computacionales de Amazon

      //developer.amazon.com/es/docs/app-submission/migrate-existing-app.html#APIsandServices

    2. Device Filtering

      <supports-screens
      android:smallScreens=»false»
      android:normalScreens=»true»
      android:largeScreens=»true»
      android:xlargeScreens=»false»
      android:requiresSmallestWidthDp=»335″/>

    3. Subir APK a Amazon App Store

      Puede ser suficiente el mismo código fuente de Google Play, compilado sin firmar.

    Más documentación: //developer.amazon.com/es/docs/app-submission/getting-started.html

  • Mi debut fue en el Somero 2015

    Muy poco antes del comienzo del Somero 2015, en la Matriz, Manuel nos vino a decir: os hemos puesto en el programa para que deis una presentación del Peersound. Me sentí alagado, pero la aplicación no estaba terminada, no habíamos probado el desarrollo en el mundo real y tampoco me sentía nada seguro con la perspectiva de explicarme ante un auditorio. Pero también me excitaba la idea, era un reto que me hacia sentir vivo. Y yo necesito sentirme vivo.

    programa someroTenía miedo. Dudaba de que pudiera hacerlo. Por pudor, no dije que no lo haría y me lancé. Creo que esto me costó una noche de insomnio. A toda prisa, instalé el tracker, el servidor web y cloné los gits que hacían falta en mi portátil del pleistoceno. Además, para facilitar la resolución de nombres de dominio, configuré un microrouter WRTnode con entradas en el DNS apuntando a los servidores. Me llevé en la maleta el hardware que necesitaba para un intercambio de archivos P2P en el navegador.

    Una vez en el Somero, antes de que empezara el taller, espanté casi todos los miedos que tenía, excepto uno: sabía que era poco probable que la demo funcionara. Primero, hubo problemas con el punto de acceso que llevé. Luego, el intercambio de archivos sólo lo hacia en contadas ocasiones. Carlos habló casi todo el tiempo, y lo hizo muy bien. Explicó la idea, que era lo más importante, y llegó. Yo hable de la parte más técnica. Estaba un poco nervioso y había una luz que me apuntaba a la cara, no sabía si se entendía algo de lo que decía. Y, al final, ocurrió lo que todo el mundo espera que ocurra en una demo: que no funcione bien. Problemas de los imprevistos. Pero para ser mi debut, en suma, no estuvo mal.

    Era la primera vez que estaba en un situación así. Compartí “plató” con Manuel, con Mikael, desarrollador principal de GNU Social y con Hannes, desarrollador de la interface Qvitter. Curiosamente, esta circunstancia no me puso más nervioso. Compartir escenario con tamaños creadores, aumento mi confianza: si yo estaba allí, era por algo. Da que pensar. Supongo que ello picaría a Mikael y Hannes. En todo caso, podrían haber prescindido de nuestra presentación y no lo hicieron. La noche anterior había hablado con Mikael sobre Bitorrent en el paseo marítimo de Gijón, mientras íbamos a algún sitio para cenar (bondades del formato del Somero 2015) y entonces se me ocurrió una estrategia: enfocar la explicación sólo para la gente que había allí. No hablé para un auditorio indefinido, no para los hackers del mundo, ni nada así. Es decir, para personas que puedo decir que las conozco.

    La demo parece que gustó. Yo no quedé del todo satisfecho con su funcionamiento, pero también hubo quién dijo que lo petamos. Aunque, para mí, lo más importante del Somero es lo que confesaba el otro día, también aprendí otras cosas muy importantes. Me doy cuenta de que, con el desarrollo de Peersound, he aprendido sobre programación más que en docenas de cursos de los que he hecho. Sé que puedo resolver, a veces, problemas que parecen imposibles. Ahora me resulta más difícil tirar la toalla. Aprendí a confiar más en mí.

    En este momento y desde hace un días, el desarrollo de Peersound se encuentra parado por un inconveniente que llevará un tiempo resolverlo. Pero sé que lo conseguiremos.

    Gracias por habernos incluido en el programa. Para la próxima, esperamos que el Peersound funcione a pleno rendimiento 😉

  • Peersound

    Se acerca el Somero 2015 y ya está todo preparado. Nos esperan unos días de amistad, comidas en común, charlas y talleres. Por fin, voy a conocer a los buenos de los indianos y a otra gente de La Matriz, donde surgió la idea de alquilar un piso compartido en Gijón para esos días.

    También en La Matriz conocí a Carlos Sanmartín, un desarrollador de sistemas empotrados, músico y quién sabe qué más, e iniciamos el desarrollo de Peersound, una aplicación de intercambio P2P de archivos de tipo audio en Javascript sobre el protocolo de Bittorrent que funciona en los navegadores Firefox y Chromium y tiene un reproductor de música para escuchar las canciones.

    guitarra

    Nuestra idea es que sea útil a las productoras pequeñas y para intercambiar música efímera, como Jam Sessions. Queremos aportar a la revolución en la industria de la música distribuida.

    Peersound almacena los archivos descargados en formato blob en la base de datos del navegador IndexedDB para sembrar las semillas. Utiliza las bibliotecas Webtorrent, Localforage y Bootstrap, entre otras. Descarga canciones con un magnetlink y puede empezar a sembrar a partir de una canción que el usuario tenga en su disco diro. Guarda las listas de reproducción en un archivo .json para reproducirlas posteriormente. Si al cargar una lista de reproducción alguno de las archivos no está en la base de datos del navegador, la descarga automáticamente de otros pares, por lo que son listas idóneas para intercambiarlas entre oyentes.

    Peersound está en fase borrador. Es decir, no es una aplicación ni mucho menos terminada. La presentamos así porque la daremos a conocer informalmente en el Somero 2015 y para que os hagáis una idea de por dónde irán los tiros. Implementaremos más formatos de audio, un buscador de canciones, metadatos completos, portadas de discos y de ser posible un reproductor para conseguir el streaming sobre bittorrent, tarea nada desdeñable.

    Decir también que hace tres meses no tenía ni idea de Javascript. Para ser completamente honesto, en mi caso no soy un desarrollador profesional y la aplicación, además de los motivos mencionados más arriba, empezó siendo una excusa para iniciarme con la programación en el mundo real.

    Espero que os guste. Aquí os dejo el git, alojado en un servidor cortesía de Las Indias.

    http://bitujo.enkidu.coop/gustavo/PeerSound

    ¡Nos vemos!

  • Gulp con Browserify, Watch y servidor Express.

    Por viernes, 18 septiembre, 2015 0 , Permalink

    Con Gulp tenemos disponible un build system con streams haciendo uso de las pipes de node.js. Es realmente útil para agilizar el desarrollo de aplicaciones de Javascript y se ha convertido en imprescindible para mí en muy poco tiempo.

    En el siguiente post, veremos como crear un archivo de configuración para construir desde los archivos fuente de desarrollo a archivos destino los siguientes tipos:

    • HTML
    • CSS
    • Javascript

    En Javascipt definiremos la tarea de browsificar modulos de node.js para cargarlos en el lado del navegador.

    El script también incluirá la tarea watch para detectar los cambios en los archivos fuente y un servidor con una función de recarga automática para actualizar el navegador sin necesidad de recargar manualmente el documento.

    Empezando, HTML

    En primer lugar ejecutamos en el directorio raíz de la aplicación el comando:

    $npm init

    y esto nos crea un archivo JSON necesario para utilizar gulp con la aplicación.

    Creamos un archivo llamado gulpfile.js y en él editaremos las tarea que serán llevadas a cabo por gulp.

    Requerimos la carga el módulo llamado gulp:

    var gulp = require('gulp');
    var source = require('vinyl-source-stream');

    Si el módulo no está instalado, hay que instalarlo introduciendo un comando tal como sigue:

    $ npm install --save-dev nombredemodulo

    y creamos la tarea:

    gulp.task('html', function() {
      return gulp.src('./src/html/*.html')
        .pipe(gulp.dest('dist'))
    });

    En la línea de comandos, para lanzar la tarea:

    $gulp html

    CSS

    La tarea:

    gulp.task('styles', function() {
    return gulp.src('src/styles/*.css')
    .pipe(gulp.dest('./dist/styles'))

    Node.js con Browserify

    En primer lugar, crearemos una tarea para los archivos Javascript browseificados:

    var browserify = require('browserify');
    var html = require('html-browserify');
    var gulpif = require('gulp-if');
    var watch;
    
    gulp.task('browserify-watch', function(){
    watch = true;
    browserifyShare();
    });
    
    function browserifyShare(){
    var b = browserify({
    cache: {},
    packageCache: {},
    fullPaths: true
    });
    
    if(watch) {
    b = watchify(b);
    b.on('update', function(){
    bundleShare(b);
    });
    }
    
    b.add('./script.js');
    
    bundleShare(b);
    }
    
    function bundleShare(b) {
    b.bundle()
    .pipe(source('./script.js'))
    .pipe(gulp.dest('./dist/scripts'))
    }
    
    
    

    Una tarea para lanzar las demás

    Creamos otras tareas para llamar a las otras tareas definidas ahora mismo:

    gulp.task('build', ['html', 'styles', 'browserify-watch']);
    gulp.task('default', ['build']);

    Con esto conseguimos que la tarea por defecto cargue el resto de tareas del script.

    Para hacerlo:

    $gulp

    Observar los cambios: Watch

    Hasta ahora, para cada cambio en un archivo fuente hay que ejecutar alguna tarea con el comando gulp, pero existe el modulo watch el cual se encarga de detectar cada cambio en los archivos fuente y ejecutar la tarea necesaria para su construcción sin cargar manualmente la tarea:

    Para ello hay que hacer algunos cambios:

    var watchify = require('watchify');
    
    gulp.task('watch', ['browserify-watch'], function(){
    gulp.watch('src/html/*.html', ['html']);
    gulp.watch('src/styles/*.css', ['styles']);
    }
    
    gulp.task('build', ['html', 'styles', 'browserify-watch']);
    gulp.task('default', ['build', 'watch']);
    

    Instalando el servidor

    Creamos las siguientes variables:

    var express = require('express');
    var server;
    

    Y añadimos la tarea:

    gulp.task('server', function() {
    server = express();
    server.use(express.static('dist'));
    server.listen(8000);
    browserSync({ proxy: 'localhost:8000' });
    });

    Abrimos el servidor:

    $gulp server

    Al iniciar la tarea server se lanzará el servidor y se podrá cerrar en cualquier momento con CTRL+C en la línea de comandos. El navegador accederá a la aplicación Javascript en la dirección localhost:8000

    Recarga automática: BrowserSync

    Hasta ahora hemos conseguido avances muy majos, pero aún podemos automatizar más el desarrollo de nuestra aplicación. Haremos una función que recarga automáticamente en el navegador los archivos modificados.

    var browserSync = require('browser-sync');

    Entonces, el servidor queda modificado así:

    gulp.task('server', function() {
    server = express();
    server.use(express.static('dist'));
    server.listen(8000);
    browserSync({ proxy: 'localhost:8000' });
    });

    Al ejecutar la tarea, gulp abrirá el navegador por defecto y cargará el recurso en localhost:3000. Esto es así porque BrowserSync tiene incorporado un proxy para nuestros archivos.

    Para recargar la tarea al modificar las fuentes, puede crearse esta función:

    function reload() {
    if (server) {
    return browserSync.reload({ stream: true });
    }
    
    return gutil.noop();
    }

    Luego llamamos a la función en tuberías en cada tareas, por ejemplo, en la tarea HTML sería:

    gulp.task('html', function() {
    return gulp.src('./src/html/*.html')
    .pipe(gulp.dest('dist'))
    .pipe(reload());
    });

    para CSS:

    gulp.task('styles', function() {
    return gulp.src('src/styles/*.css')
    .pipe(gulp.dest('./dist/styles'))
    .pipe(reload());
    });

    y para Browserify:

    function bundleShare(b) {
    b.bundle()
    .pipe(source('./script.js'))
    .pipe(gulp.dest('./dist/scripts'))
    .pipe(gulpif(watch, livereload()))
    .pipe(reload());
    }

    Para llamar al proceso desde la tarea watch, ésta se puede modificar para que quedé así:

    gulp.task('watch', ['browserify-watch'], function(){
    gulp.watch('src/html/*.html', ['html']);
    gulp.watch('src/styles/*.css', ['styles']);
    
    livereload.listen(35729);

    Por último, para lanzar gulp el servidor y todas las tareas:

    $gulp

    Resumen

    A modo de resumen, el archivo gulpfile.js quedará así:

    'use strict'
    var gulp = require('gulp');
    var browserify = require('browserify');
    var source = require('vinyl-source-stream');
    var express = require('express');
    var html = require('html-browserify');
    var watchify = require('watchify');
    var livereload = require('gulp-livereload');
    var gulpif = require('gulp-if');
    var browserSync = require('browser-sync');
    var notify = require('gulp-notify')
    var util = require('gulp-util')
    var server;
    var watch;
    
    gulp.task('html', function() {
    return gulp.src('./src/html/*.html')
    .pipe(gulp.dest('dist'))
    .pipe(reload());
    });
    
    gulp.task('images', function() {
    return gulp.src('images/*.svg')
    .pipe(gulp.dest('./dist/images'))
    .pipe(reload());
    });
    
    gulp.task('styles', function() {
    return gulp.src('src/styles/*.css')
    .pipe(gulp.dest('./dist/styles'))
    .pipe(reload());
    });
    
    gulp.task('browserify-watch', function(){
    watch = true;
    browserifyShare();
    });
    
    function browserifyShare(){
    var b = browserify({
    cache: {},
    packageCache: {},
    fullPaths: true
    });
    
    if(watch) {
    b = watchify(b);
    b.on('update', function(){
    bundleShare(b);
    });
    }
    
    b.add('./script.js');
    
    bundleShare(b);
    }
    
    function bundleShare(b) {
    b.bundle()
    .pipe(source('./script.js'))
    .pipe(gulp.dest('./dist/scripts'))
    .pipe(gulpif(watch, livereload()))
    .pipe(reload());
    }
    
    gulp.task('server', function() {
    server = express();
    server.use(express.static('dist'));
    server.listen(8000);
    browserSync({ proxy: 'localhost:8000' });
    });
    
    gulp.task('watch', ['browserify-watch'], function(){
    gulp.watch('src/html/*.html', ['html']);
    gulp.watch('src/styles/*.css', ['styles']);
    
    livereload.listen(35729);
    });
    gulp.task('build', ['html', 'styles', 'browserify-watch']);
    gulp.task('default', ['build', 'server', 'watch']);
    
    function reload() {
    if (server) {
    return browserSync.reload({ stream: true });
    }
    
    return util.noop();
    }
  • Cambiar tipo MIME de archivos manifest.webapp

    Por jueves, 19 diciembre, 2013 0 Permalink

    Me he encontrado con el problema de que a la hora de instalar una aplicación de Firefox OS en el simulador, éste me daba un error de tipo de MIME, ya que mi sistema no tenía la extensión de archivo .webapp asociada al tipo MIME application/x-web-app-manifest+json, por lo tanto, no reconocía el tipo de contenido e identificaba el archivo como de tipo ASCII sin más.

    Continuar Leyendo…