Friday, August 24, 2012

Android EditText with number format input

Some applications require formatting the EditText's value while typing. I.E., a number that needs to be formatted with decimal and thousands separators.

We've created a little helper class implementing TextWatcher to do just that.

import java.text.DecimalFormat;
import java.text.ParseException;

import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;

public class NumberTextWatcher implements TextWatcher {
    
    private DecimalFormat df;
    private DecimalFormat dfnd;
    private boolean hasFractionalPart;
    
    private EditText et;
    
    public NumberTextWatcher(EditText et)
    {
        df = new DecimalFormat("#,###.##");
        df.setDecimalSeparatorAlwaysShown(true);
        dfnd = new DecimalFormat("#,###");
        this.et = et;
        hasFractionalPart = false;
    }
    
    @SuppressWarnings("unused")
    private static final String TAG = "NumberTextWatcher";

    @Override
    public void afterTextChanged(Editable s)
    {
        et.removeTextChangedListener(this);
        
        try {
            int inilen, endlen;
            inilen = et.getText().length();
            
            String v = s.toString().replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
            Number n = df.parse(v);
            int cp = et.getSelectionStart();
            if (hasFractionalPart) {
                et.setText(df.format(n));
            } else {
                et.setText(dfnd.format(n));
            }
            endlen = et.getText().length();
            int sel = (cp + (endlen - inilen));
            if (sel > 0 && sel <= et.getText().length()) {
                et.setSelection(sel);
            } else {
                // place cursor at the end?
                et.setSelection(et.getText().length() - 1);
            }
        } catch (NumberFormatException nfe) {
            // do nothing?
        } catch (ParseException e) {
            // do nothing?
        }
        
        et.addTextChangedListener(this);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after)
    {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count)
    {
        if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
        {
            hasFractionalPart = true;
        } else {
            hasFractionalPart = false;
        }
    }

}

To use it, all you need to do is add a TextChangedListener to the EditText component.

editText.addTextChangedListener(new NumberTextWatcher(editText));


You can get the source code directly from GitHub.



Friday, August 10, 2012

Parecer > Ser

Hace muchos años en circunstancias completamente diferentes, dos grandes amigos de la casa me dijeron básicamente la misma cosa:
Entre ser y parecer, siempre tenés que elegir parecer.
Victor Cálcena estaba criticando mi vestimenta y Miguel Balsevich nuestra página WEB.

Nuestra página fue migrando con los años, pero nunca fue algo a que le tomamos demasiada atención. La primera es una joya de 2002 (templatazo del venerable Frontpage de Microsoft):



La segunda es de 2004. Dio paso a un largo periodo de ausentismo en la WEB:



La tercera es de 2005. Se hizo con Metadot. No tenemos archivos de esa página, pero en algún lado debe estar. La cuarta es de 2008. Una página minimalista que siguió viva hasta hoy:





Ya hora, sin más preámbulo, les presentamos nuestra nueva página web. Con esta, empezamos a parecer un poquito más que antes.




De los detalles técnicos, le vamos a pedir a Gustavo que nos cuente en una entrada más adelante. El excelente diseño es de nuestro diseñador estrella Diego Garcete.

Monday, July 16, 2012

Roshka participa de WSA Paraguay 2012

Qué es WSA? De la WEB del concurso:

WSA (WORLD SUMMIT AWARDS) ES UNA CONFERENCIA Y PREMIO MUNDIAL CREADO PARA PROMOVER Y SELECCIONAR LOS MEJORES CONTENIDOS ELECTRÓNICOS Y APLICACIONES TECNOLÓGICAS CON EL OBJETIVO DE TENER UNA SOCIEDAD DE LA INFORMACIÓN MÁS INCLUSIVA.

Este es el sitio WEB internacional oficial, y este es el sitio WEB de Núcleo S.A. que organiza dicho concurso para el Paraguay.

Nosotros nos presentamos este año con 3 aplicaciones:


  • Bubble Drill (juego para iOS)
Bubble Drill es un juego muy divertido y adictivo que consiste que agrupar burbujas del mismo color para reventarlas y evitar que un gran taladro aplaste nuestro mundo.

Disponible de manera gratuita en el AppStore de Apple. Más información, acá.


  • Red Social Roshka (aplicación de Redes Sociales para ser utilizada desde celulares con SMS y MMS)
Red Social Roshka es una aplicación que permite  la conexión a redes sociales (Facebook, Twitter) en celulares usando tecnología SMS / MMS, por lo tanto habilitando a personas que no tienen acceso a la red de DATOS o a un SMARTPHONE acceder a servicios de redes sociales.

Sus principales funcionalidades son:

-FacebookChat
-Twitter
-Envio/recepción de fotos de facebook x MMS.
-Cambio de Status de Facebook.
-Cambio de Status en Twitter

Disponible en Facebook. Acá hay información de cuando la lanzamos al mercado.


  • iFogOfWar (juego para iOS)
iFogOfWar es un juego de estrategia para dos jugadores basado en el popular juego de tablero Stratego. Usa un tablero de 10x10 donde equipos con 40 piezas cada uno pelean por capturar la bandera enemiga.

Disponible de manera gratuita en el AppStore de Apple. Más información, acá.



    ------------------------------

    A través de esta entrada en el BLOG, queremos invitar a todas las empresas colegas a publicar sus producciones, para que este tipo de iniciativas siga haciendo crecer nuestra incipiente Industria Nacional del Software paraguayo.

    Y por supuesto, queremos pedirles que nos den sus votos (en forma de LIKEs en Facebook) para que tengan mayor consideración de parte del jurado internacional. Pueden bajar e instalar las aplicaciones sin problemas, son gratuitas. Además, los enlaces para votar:

    Gracias a todos de antemano.

    Saturday, July 7, 2012

    HTTP POST commands within Oracle's PL/SQL

    Sometimes for system integration it's pretty interesting to perform POST (or GET) HTTP request within Oracle's PL/SQL. It's pretty straightforward, but I have a hard time looking for a specific example (at least for POST) in Oracle's documentation.

    OK. But why on earth you would like to use PL/SQL to perform a HTTP POST request!? Turns out, there still are a lot of software shops or businesses with a software department with extremely highly skilled Oracle developers, that need to focus on solving complex business issues with a database instead of learning the new cool programming language that you can use to do this exact same task with two lines of code. For them, a PL/SQL wrapper around a RESTful API is pretty convenient.

    So, here's what I did to perform a POST passing two parameters along:

    create or replace
    package wsaccess_pkg as
    
      /* 
         suppose your RESTful API requires a POST with two parameters: id, name  
         and it returns a string containing an xml
      */
      
      -- id number
      -- name string
      
      function executews
      (
          p_id number,
          p_name varchar2
      )
      return varchar2;
      
    end wsaccess_pkg;
    
    create or replace
    package body wsaccess_pkg as
    
      function executews
        (
          p_id number,
          p_name varchar2
        )
      return varchar2 AS
        postData clob;
        xmlData clob;
        v_url varchar2(500);
        v_msg varchar(512);
        req utl_http.req;
        resp utl_http.resp;
      begin
      
        utl_http.set_response_error_check(enable => TRUE);
    
        -- this is your RESTful API's URL where you would POST data
        v_url := 'http://www.example.com/api/scoreCard';
    
        postData := '';
        postData := postData || 'id=' || p_id || '&';
        postData := postData || 'name=' || p_name || '&';
        
        -- begin request stating that method will be POST
        req := utl_http.begin_request(url => v_url, method => 'POST');
        
        -- setup request's headers. you need both: Content-Type and Content-Length
        utl_http.set_header(req, 'Content-Type', 'application/x-www-form-urlencoded');
        utl_http.set_header(req, 'Content-Length', length(postData));
        
        -- optionally, you might want to set User Agent header
        utl_http.set_header(req, 'User-Agent', 'MyCoolHttpClientFromWSAccessPkg/1.5');
        
        -- send data: this function handles URL encoding for us!
        utl_http.write_text(req, postData);
        
        -- read the response
        resp := utl_http.get_response(r => req);
        xmlData := '';
        begin
          loop
            utl_http.read_text(r => resp, data => v_msg);
            xmlData := xmlData || v_msg;
          end loop;
        exception
          when utl_http.end_of_body then
            null;
        end;
        
        -- end response
        utl_http.end_response(r => resp);
        
        return xmlData;
      end executews;
    
    end wsaccess_pkg;
    

    You could also parse the XML response and return a pure PL/SQL object. That's even better. But I would probably talk about in a separate blog entry.

    Friday, June 15, 2012

    Restoring a lost crontab

    I recently issued a command like this on a production server:

    $ crontab -r

    I really meant to issue this command instead:

    $ crontab -e

    (I was trying to add an entry to a very important crontab).

    Needless to say, after the command I mistakenly executed, my crontab was gone. 

    From crontab's man page:
           -r     The current crontab will be removed.
    I went to google to search for some answers and here's what I had to do in order to restore my removed crontab file (I am using CentOS 5):

    First, there is directory in /var/spool/cron which holds all crontab files for all users. Mine was gone. So you can't get it from there unless you have a backup (which I didn't). So I couldn't t use it to restore my crontab.

    But, there is a log file which turned out to be pretty useful. They are located in /var/logs. The file names are 'cron*'.

    So I copied them to a folder to avoid working directly on them:

    # cp /var/logs/cron* /home/myuser/cron_restore/
    # chown -R myuser:myuser /home/myuser/cron_restore/

    Then I extracted all different commands that had been running by crond:

    $ cd /home/myuser/cron_restore/
    $ cat cron* | grep crond | awk '{ split($0, a, "CMD "); print a[2]; }' | sort | uniq

    After running the command, I got a list of all different command run and logged by the cron daemon. Of course, this will only bring you the commands that were still present on the log files. But it happened that I got to recover all I needed. Don't know if it will be the same for someone logging some stuff on a yearly or even monthly basis.

    Hope you find this entry useful.

    Wednesday, April 25, 2012

    SQLite como cache de Oracle


    No hace mucho, trabajando en un proyecto para una empresa, tuvimos un problema de performance con el tiempo de respuesta al momento de realizar queries sobre la base de datos Oracle. Luego de varias mediciones del tiempo de respuesta y estadísticas sobre queries que mayor tiempo de respuesta consumían y no consiguiendo mejorar ostensiblemente con la optimización de los queries, decidimos, a sugerencia del gurú Pablo “Calé” Santa Cruz, investigar y llevar a la práctica la utilización de SQLite como cache de datos. De aquí en adelante hablaremos de que se hizo y como se hizo para lograr la optimización, especialmente cuando se trata con volúmenes interesantes de datos.

    Para la implementación de esta mejora recurrimos a un driver jdbc nativo de java para SQLite y la versión que utilizamos es sqlitejdbc-v056.jar.

    Antes del inicio del proceso, primeramente realizamos un volcado completo de los datos de la tabla en donde los queries arrojaban tiempos moderadamente altos, a nuestra base de datos SQLite que actuará de cache, y que al tratarse de consultas iterativas, representaban un tiempo total considerablemente costoso en todo el proceso. Es importante destacar en este punto que la replicación de los datos se realizó en un tiempo bastante menor del que esperábamos para sorpresa nuestra, primera buena impresión.

    Ya con los datos enteramente replicados en una tabla temporal de nuestra base de datos SQLite nos detuvimos a medir los tiempos de respuesta de los queries sobre nuestro cache y arrojaron resultados sorprendentes, por cada proceso iterativo, hemos ahorrado aprox. 86% del tiempo de respuesta o lo que equivale a 1/6 del tiempo de procesamiento cuando la consulta se realizaba puramente sobre la base de datos Oracle.

    En conclusión, cuando se trata de manejar volúmenes grandes de datos y el tiempo de respuesta de la base de datos es crítico en procesos iterativos y por sobre todo elevado, es conveniente recurrir a este tipo de implementación que nos permita mejorar ostensiblemente el tiempo de procesamiento.

    Thursday, April 12, 2012

    Sobre chipas y la nube de Google


    Mi nombre es Lucas y soy parte del equipo de desarrolladores de Roshka.
    A modo de fortalecer los vínculos sociales, una costumbre convertida en tradición dentro del círculo de programadores de la empresa, es hacer pedidos de chipa los días viernes.
    Papel y lápiz en mano, la persona encargada de organizar las ordenes de chipa del día recorría todas las oficinas recolectando pedidos. Concluida esa primera fase del proceso, el organizador del pedido se dirigía a la chiperia para hacer las compras.
    El sistema, si bien altamente efectivo, carecía del “factor ornamental” que idealmente debiera regir todo orden de eventos dentro una empresa informática: la sistematización del procedimiento.
    Concernido por este hecho, poco más de cinco meses atrás me propuse el objetivo de desarrollar una aplicación web para automatizar el antaño procedimiento de pedidos de chipa como pretexto para iniciarme en el aprendizaje de Python y la incursión en una tecnología que viene acrecentando su peso en el presente de la futura vanguardia informática: el cloud computing.
    Chipas-inthecloud (chipas-inthecloud.appspot.com) es el resultado de la combinación de ambos factores. Hosteado en la nube de servidores de Google, el sistema fue desarrollado íntegramente en Python mediante el servicio PaaS Google App Engine.
    Una resumida referencia a las características, ventajas y restricciones del servicio puede encontrarse en el siguiente recurso:
    A mi criterio, lo más rescatable del App Engine es el alto nivel de abstracción ofrecido respecto a factores no siempre ágilmente implementables como ser la interacción con las cuestiones relacionados a la infraestructura y el manejo de los servicios y tecnologías propias de la plataforma de hospedaje, permitiendo al programador enfocarse puramente en la lógica de negocios y el subsecuente desarrollo del sistema.
    El manejo de persistencia de la aplicación se concretó a través del API de almacenamiento de datos NoSQL provisto por el entorno de desarrollo del App Engine. Los reportes fueron construidos mediante una librería Python para la generación programática de documentos PDF, llamada Reportlab. Chipas-inthecloud hace uso de los servicios de autenticación, envío de mails y mensajes XMPP nativos del AppEngine. Una actividad cron que se ejecuta cada minuto controla el tiempo de expiración de los pedidos, para enviar automáticamente las notificaciones correspondientes a los usuarios que participaron del mismo.
    Captura de chipas-inthecloud corriendo en el localhost y no en la nube



    Lejos aún de dominar Python y sin poder apreciarme el merito de haber escrito un sistema libre de potenciales fallas, puedo concluir que la experiencia de desarrollo fue muy gratificante. La documentación existente del AppEngine facilita un proceso de formación muy ágil. La puesta en producción de una aplicación escrita desde cero admite estándares de relación tiempo/esfuerzo imponderables. GAE es una de las mejores ofertas existentes en el mercado para todos aquellos con deseos de incursionar en el desarrollo de aplicaciones web en la nube.