Google Cloud Messaging (CGM) y nodejs

Los dispositivos móviles forman parte de nuestro día a día, ademas los smartphones son una herramienta valiosa que llevar siempre encima. La posibilidad de enviar notificaciones push a estos es una excelente forma de mantener conectadas nuestras aplicaciones. Aprovechando que recientemente hemos hecho una integración android para Canaltonight, vamos a ver un ejemplo mínimo usando nodejs y una aplicación de ejemplo del sdk de android.

GCM es un servicio que ofrece google para dispositivos android que nos permite enviar pequeños mensajes a nuestras aplicaciones de una forma estandarizada y cómoda.

Necesitamos una aplicación que será la que nos permita usar el servicio de google. Podemos ver este proceso descrito en la guía de inicio del servicio. Necesitaremos dos valores importantes el Sender ID y una API Key.

Ahora necesitamos instalar eclipse que podemos descargar de su web. Bajaremos el que pone Eclipse IDE for Java Developers. Los instalamos.

Descargamos el sdk de android, y lo descomprimimos en algun sitio de nuestro disco duro.

Ahora abrimos el eclipse, y vamos a “Help > Install New Software”. Después picamos en el botón “Add” de la esquina superior derecha. En el dialogo que se nos abre ponemos en “Name” “ADT Plugin” y en “Location”
https://dl-ssl.google.com/android/eclipse/ hacemos click en “OK”.

Marcamos el “Developers Tools” y vamos de “Next” a “Next” hasta “Finish” y reiniciamos eclipse. Más información.

Cuando se reinicie nos pedirá el SDK de android y se lo indicaremos desde donde lo hayamos puesto.

Una vez se haya iniciado, vamos a “Window” -> “Android SDK Manager” e instalamos los paquetes necesarios

  • Android 2.3.3 (API 10)
    • SDK Platform
    • Samples for SDK
    • Google APIs
  • Extras
    • Google Cloud Messaging for Android

Picamos en el boton de Install. Una vez que tengamos todo preparado podemos empezar con la aplicación android.

Vamos a File -> New -> Other …, dentro del dialogo, Android -> Android Project from Existing Code, Y picamos en “Next”. Picamos en Browse…, buscamos el sdk de android extras/google/gcm/samples/gcm-demo-client.
Seleccionamos “Copy projects into workspace” y picamos en “Finish”

Ahora disponemos de una pequeña aplicación que es capaz de conectar dispositivos con GCM y registrarse contra un servidor web. Nosotros tras finalizar con la app android construiremos una mini app usando expresjs y gcm-node para enviar mensajes a estos dispositivos.

Hay que modificar pocas cosa, primero en src/com.google.android.gcm.demo.app/CommonUtilities.java configuramos nuestra aplicación. Necesitaremos configurar la url base de la app node donde se registrará el cliente android y nuestro ID de GCM que conseguimos al principio.

/**
 * Base URL of the Demo Server (such as http://my_host:8080/gcm-demo)
 */
static final String SERVER_URL = null;

/**
 * Google API project id registered to use GCM.
 */
static final String SENDER_ID = null;

La SERVER_URL en una red local podría ser algo como http://192.168.0.101:3000 el SENDER_ID es el código numérico de nuestra aplicación google.

Ahora vamos a añadir que se muestren los mensajes que enviemos a traves de gcm, para ello vamos a asumir que los enviaremos en un campo llamado message.
Abrimos src/com.google.android.gcm.demo.app/GCMIntentService.java y añadimos que se muestre tambien el mensaje que enviaremos desde nuestra aplicación node en un campo message.

@Override
protected void onMessage(Context context, Intent intent) {
    Log.i(TAG, "Received message");
    String message = getString(R.string.gcm_message);
    displayMessage(context, message);
    // Show message field of message
    displayMessage(context, intent.getExtras().getString("message"));
    // notifies user
    generateNotification(context, message);
}

Guardamos todo y lo ejecutamos en nuestro terminal conectado. Debería mostrarnos que se registro correctamente en gcm pero que no consigue conectar con nuestro servidor, ¡claro! todavía no existe :P

Servidor

Vamos a crear una pequeña aplicación para enviar mensajes a estos telefonos ansiosos por recibirlos. Para ello nos serviremos de express para prepararla en pocos pasos.

Instalamos express

npm install -g express@3.0

Creamos la aplicación

express gcm-server

Editamos el package.json para añadir los módulos, gcm-node, para conectar con el servicio de google con nuestra API key, y underscore, para suavizarnos el trabajo.

{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app"
  },
  "dependencies": {
    "express": "3.0.0rc4",
    "jade": "*",
    "gcm-node": "*",
    "underscore": "*"
  }
}

ahora instalamos todo

npm install

Nuestra aplicación constará de 4 rutas:

/

donde mostraremos un listado de los dispositivos conectados y un formulario para enviarles los mensajes.

/register

end point donde nuestra app android se registrará

/unregister

end point donde nuestra app android podrá darse de baja de nuestro servidor

/send acción

el formulario para enviar los mensajes

Editamos app.js modificados las rutas

app.get( '/',           routes.index);
app.post('/register',   routes.register);
app.post('/unregister', routes.unregister);
app.post('/send',       routes.send_message);

Después en routes/index.js

var gcm = require('node-gcm')
  , _ = require('underscore')
  , registrationIds = []
  , sender = new gcm.Sender('YOUR-API-KEY');

/*
 * GET home page.
 */

exports.index = function(req, res) {
 res.render('index', {
   title: 'Express',
   receptors: registrationIds
 });
};

exports.register = function(req, res) {
  registrationIds.push(req.body.regId);
  res.send(200);
};

exports.unregister = function(req, res) {
  registrationIds = _.without(registrationIds, req.body.regId);
  res.send(200);
};

exports.send_message = function(req, res) {
  var message;
  message = new gcm.Message;
  message.addData('message', req.body.message);
  _.intersection(registrationIds, req.body.receptors.split(','));
  sender.send(message, registrationIds, 4, function(result) {
    console.log(result);
  });
  res.redirect('/');
};

Usaremos un array para almacenar los identificadores de los dispositivos para poder mandarles los mensajes. No parece la solución mas robusta del mundo pero nos servirá para conectar todas las piezas y comprobar que funciona.

Las funciones register y unregister añaden y eliminan el id que recibe en el parametro regId. send_message sirve de action para el formulario que añadiremos ahora para enviar los mensajes, en el podrás seleccionar una serie de ids y enviarles un mensaje dado. Se lo añadimos a views/index.jade

...
p Welcome to #{title}
form(action="/send",method="post")
  p
    select(name="receptors",multiple="multiple")
      for recep in receptors
        option(value="#{recep}")= recep.slice(0,24)+"..."
  p
    textarea(name="message")
  p
    input(type="Submit")

Ya tenemos todo listo. Si arrancamos la aplicación con

node app.js

Y nos dirigimos a http://localhost:3000, veremos nuestro ui vacío

Si rearrancamos la app veremos si lo tenemos todo bien como engancha con nuestro servidor:

Ahora recargando el home de nuestra web, veremos el id del device y seleccionandolo podremos enviarle nuestro mensaje

Ya tenemos funcionando nuestro sistema de notificación de mensajes push a nuestra app android desde node.js.

Happy push coding :)

  • Agus

    que es Express?

  • Jhon Erick

    excelente tutorial amigo, sera que me puedes colaborar cuando escribo un mensaje y le doy enviar me sale error en express tendrás alguna idea de lo que estoy haciendo mal?

    el la 1 imagen muestro que me carga bien y en celu se conecta pero al escribir y dar en aceptar pailas imagen2 de antemano gracias por tu blog ;)

    • cartuchogl

      Por lo que veo en captura, parece que la llamada a /send se hace por GET, pero cuando se configura en el routes/index.js espera que sea post, comprueba que el formulario en views/index.jade tiene method=”post”.

      • Jhon Erick

        gracias por tu atenta colaboración y se que es muy difícil tratar de encontrar un error de esta forma.
        mira esta es la consola de inicio a fin y el error lo he localizado en esta linea de código

        _.intersection(registrationIds, req.body.receptors.split(‘,’));

        el proyecto esta tal cual lo subiste y sin el animo de ofenderme a mi mismo es 100% copy y paste, después le haré las modificaciones necesarias. el asunto es que no me funciona al enviar el mensaje al celu, mi única esperanza es que tal ves recuerdes si a el proyecto le falta agregar algo para hacerlo funcional.
        pdt: le hice un consolé a req antes de ser ejecutado en la linea de código ya mencionada y encontré el método body pero no receptors de antemano muchas gracias

        • Jhon Erick

          Solucionado era una boludes faltaba seleccionar una opción del select xD!