jueves, 21 de febrero de 2013

Otro Cliente de Push Notification GCM

En esta ocasion, les quiero compartir la implementacion de un Cliente Android para las Notificaciones Push, tomado del sitio web Sgoliver .Net , un blog de Salvador Gómez. Dejo el texto con las imagenes tal cual está en el sitio web original.


Esta aplicación cliente, como ya hemos comentado en alguna ocasión será responsable de:
  1. Registrarse contra los servidores de GCM como cliente capaz de recibir mensajes.
  2. Almacenar el “Registration ID” recibido como resultado del registro anterior.
  3. Comunicar a la aplicación web el “Registration ID” de forma que ésta pueda enviarle mensajes.
  4. Recibir y procesar los mensajes desde el servidor de GCM.
Para las tareas 1 y 4 utilizaremos una librería específica de GCM que nos proporciona Google para facilitarnos la vida como desarrolladores (es posible hacerlo sin el uso de librerías externas, aunque requiere más trabajo). El punto 2 lo resolveremos fácilmente mediante el uso de SharedPreferences. Y por último el punto 3 lo implementaremos mediante la conexión al servicio web SOAP que creamos en el artículo anterior, sirviéndonos para ello de la librería ksoap2, tal como ya describimos en los artículos sobre servicios web SOAP en Android.
Durante el artículo construiremos una aplicación de ejemplo muy sencilla con el siguiente aspecto:
Aplicación Ejemplo Android GCM
Aplicación Ejemplo Android GCM
En esta aplicación, el usuario podrá introducir un nombre de usuario identificativo y pulsar el botón “Aceptar” para que quede guardado en las preferencias de la aplicación. Tras esto podrá registrarse como cliente capaz de recibir mensajes desde GCM pulsando el botón “Registrar GCM”. En caso de realizarse de forma correcta este registro la aplicación enviará automáticamente el Registration ID recibido y el nombre de usuario almacenado a la aplicación web a través del servicio web. Igualmente el usuario podrá des-registrarse en el servicio GCM para no recibir más mensajes pulsando el botón “Des-registrar GCM”. Obviamente todo este proceso de registro y des-registro debería hacerse de forma transparente para el usuario de una aplicación real, en esta ocasión he colocado botones para ello sólo por motivos didácticos.
Antes de nada vamos a preparar nuestro proyecto de Eclipse y vamos a configurar convenientemente el AndroidManifest para poder hacer uso del servicio GCM y su librería auxiliar.
Para ello vamos a crear un nuevo proyecto Android sobre un target de Android 2.2 o superior que incluya las librerías de Google, y vamos a incluir en su carpeta /libs las librerías de ksoap2 (esto ya vimos como hacerlo en el artículo sobre servicios web) y la librería cliente de GCM llamada “gcm.jar“. ¿Cómo podemos obtener esta librería? Para conseguirla debemos instalar desde el Android SDK Manager el paquete extra llamado “Google Cloud Messaging for Android Library”.
Descarga librería Google Cloud Messaging for Android
Librería Google Cloud Messaging for Android
Una vez instalado podemos ir a la ruta “RAIZ_SDK_ANDROID/extras/google/gcm/gcm-client/dist” donde deberá aparecer la librería “gcm.jar” que debemos añadir a nuestro proyecto.
Lo siguiente será configurar nuestro AndroidManifest. Lo primero que añadiremos será una cláusula <uses-sdk> para indicar como versión mínima del SDK soportada la 8 (Android 2.2). Con esto nos aseguraremos de que la aplicación no se instala en dispositivos con versión de Android anterior, no soportadas por el servicio GCM.
1
2
3
<uses-sdk
     android:minSdkVersion="8"
     android:targetSdkVersion="16" />
A continuación añadiremos los permisos necesarios para ejecutar la aplicación y utilizar GCM:
1
2
3
4
5
<permission android:name="net.sgoliver.android.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="net.sgoliver.android.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Los dos primeros aseguran que sólo esta aplicación podrá recibir los mensajes, el segundo permite la recepción en sí de mensajes desde GCM (sustituir mi paquete java “net.sgoliver.android” por el vuestro propio en estas lineas), el tercero es el permiso para poder conectarnos a internet y el último es necesario para tareas realizadas durante la recepción de mensajes que veremos más adelante.
Por último, como componentes de la aplicación, además de la actividad principal ya añadida por defecto, deberemos declarar un broadcast receiver llamado GCMBroadcastReceiver, que no tendremos que crearlo porque ya viene implementado dentro de la librería “gcm.jar” (solo tenéis que modificar el elemento <category> para indicar vuestro paquete java), y un servicio llamadoGCMIntentService (Atención, es obligatorio este nombre exacto para el servicio si no queremos tener que implementar nosotros mismos el broadcast receiver anterior). Ya veremos más adelante para qué son estos dos componentes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
    ...
    <receiver android:name="com.google.android.gcm.GCMBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="net.sgoliver.android" />
        </intent-filter>
    </receiver>
    <service android:name=".GCMIntentService" />
</application>
Una vez definido nuestro AndroidManifest con todos los elementos necesarios vamos a empezar a implementar la funcionalidad de nuestra aplicación de ejemplo.
Empezamos por la más sencilla, el botón de guardar el nombre de usuario. Como comentamos anteriormente, vamos a utilizar preferencias compartidas para esta tarea. Como éste es un temaya visto en el curso no me detendré en el código ya que es bastante directo.
1
2
3
4
5
6
7
8
9
10
11
btnGuardarUsuario.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        SharedPreferences prefs =
             getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString("usuario", txtUsuario.getText().toString());
        editor.commit();
    }
});
Como podéis comprobar nos limitamos a almacenar una nueva propiedad llamada “usuario” con el texto introducido en el cuadro de texto de la interfaz.
El siguiente botón es el de registro del cliente en GCM, y aquí sí nos detendremos un poco para comentar primero cómo funciona internamente este procedimiento.
La aplicación android debe solicitar el registro en el servicio GCM mediante una petición HTTP POST similar a la que ya vimos en el artículo anterior para la aplicación web. Por suerte, este procedimiento se ve simplificado enormemente con el uso de la librería gcm.jar, ya que el montaje y ejecución de esta petición queda encapsulado como una simple llamada a un método estático de la clase GCMRegistrar, definida en la librería. Por su parte, tanto la respuesta a esta petición de registro como la posterior recepción de mensajes se reciben en la aplicación Android en forma de intents. Y aquí es donde entran en juego los dos componentes que hemos definido anteriormente en nuestro AndroidManifest. El receiver GCMBroadcastReceiver será el encargado de “esperar y capturar” estos intents cuando se reciban y posteriormente lanzar el servicio GCMIntentService donde se procesarán en un hilo independiente estas respuestas según el intent recibido. Como ya dijimos, el broadcast receiver no será necesario crearlo ya que utilizaremos el ya proporcionado por la librería. En cambio la implementación delIntentService sí será de nuestra responsabilidad. Aunque una vez más la librería de GCM nos facilitará esta tarea como ya veremos más adelante.
Veamos primero cómo realizar el registro de nuestra aplicación en GCM al pulsar el botón de registro. Como hemos dicho esto se limitará a llamar a un método estático, llamado register(), de la clase GCMRegistrar. La única precaución que tomaremos es verificar previamente si estamos ya registrados, algo que podremos hacer fácilmente llamando al métodogetRegistrationId() de la misma clase. El método register() recibirá dos parámetros, el primero de ellos una referencia al contexto de la aplicación (normalmente la actividad desde la que se llama) y en segundo lugar el “Sender ID” que obtuvimos cuando creamos el nuevo proyecto en la Google API Console.
1
2
3
4
5
6
7
8
9
10
11
12
13
btnRegistrar.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        //Si no estamos registrados --> Nos registramos en GCM
            final String regId = GCMRegistrar.getRegistrationId(GcmActivity.this);
            if (regId.equals("")) {
                GCMRegistrar.register(GcmActivity.this, "224338875065"); //Sender ID
            } else {
                Log.v("GCMTest", "Ya registrado");
            }
    }
});
Así de sencillo y rápido. Pero esto es sólo la petición de registro, ahora nos tocará esperar la respuesta, algo que veremos en breve.
Por su parte, el botón de “des-registro” se implementará de forma análoga, con la única diferencia que esta vez utilizaremos el método unregister() de la clase GCMRegistrar.
1
2
3
4
5
6
7
8
9
10
11
12
13
btnDesRegistrar.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        //Si estamos registrados --> Nos des-registramos en GCM
            final String regId = GCMRegistrar.getRegistrationId(GcmActivity.this);
            if (!regId.equals("")) {
                GCMRegistrar.unregister(GcmActivity.this);
            } else {
                Log.v("GCMTest", "Ya des-registrado");
            }
    }
});
Ahora toca procesar las respuestas. Como hemos dicho, para hacer esto tendremos que implementar el servicio GCMIntentService. Pero no lo haremos desde cero, ya que la librería de GCM nos proporciona una clase base GCMBaseIntentService de la cual podemos extender la nuestra, con la ventaja de que tan sólo tendremos que sobrescribir unos pocos métodos a modo de callbacks, uno por cada posible respuesta o mensaje que podemos recibir desde el servicio GCM. Estos métodos son:
  • onRegistered(context, regId). Se llamará al recibirse una respuesta correcta a la petición de registro e incluye como parámetro el Registration ID asignado a nuestro cliente.
  • onUnregistered(context, regId). Análogo al anterior pero aplicado a una petición de “des-registro”.
  • onError(context, errorId). Se llamará al recibirse una respuesta de error a una petición de registro o des-registro. El código de error concreto se recibe como parámetro.
  • onMessage(context, intent). Se llamará cada vez que se reciba un nuevo mensaje desde el servidor de GCM. El contenido del mensaje se recibe en forma de intent, el cual veremos más adelante cómo procesar.
Empecemos por el método onRegistered(). Al recibir una respuesta satisfactoria a la petición de registro recuperaremos el nombre de usuario almacenado y junto con el Registration IDrecibido nos conectaremos al servicio web que creamos en el artículo pasado pasándole dichos datos. Esto completará el registro tanto con el servidor de GCM como con nuestra aplicación web.
1
2
3
4
5
6
7
8
9
10
protected void onRegistered(Context context, String regId) {
    Log.d("GCMTest", "REGISTRATION: Registrado OK.");
    SharedPreferences prefs =
         context.getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE);
    String usuario = prefs.getString("usuario", "por_defecto");
    registroServidor(usuario, regId);
}
El método registroServidor() será el encargado de realizar la conexión al servicio web y de la llamada al método web de registro. No me detendré en comentar el código de este método porque es análogo a los ejemplos ya vistos en el artículo que dedicamos a servicios web SOAP en Android. Veamos tan sólo el código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private void registroServidor(String usuario, String regId)
{
    final String NAMESPACE = "http://sgoliver.net/";
    final String METHOD_NAME = "RegistroCliente";
    final String SOAP_ACTION = "http://sgoliver.net/RegistroCliente";
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
    request.addProperty("usuario", usuario);
    request.addProperty("regGCM", regId);
    SoapSerializationEnvelope envelope =
        new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.dotNet = true;
    envelope.setOutputSoapObject(request);
    HttpTransportSE transporte = new HttpTransportSE(URL);
    try
    {
        transporte.call(SOAP_ACTION, envelope);
        SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse();
        String res = resultado_xml.toString();
        if(res.equals("1"))
            Log.d("GCMTest", "Registro WS: OK.");
    }
    catch (Exception e)
    {
        Log.d("GCMTest", "Registro WS: NOK. " + e.getCause() + " || " + e.getMessage());
    }
}
Por su parte, en los métodos de des-registro y de error me limitaré a escribir un mensaje en el log de la aplicación para no complicar demasiado el ejemplo, pero en una aplicación real deberíamos verificar estas respuestas.
1
2
3
4
5
6
7
8
9
@Override
protected void onUnregistered(Context context, String regId) {
    Log.d("GCMTest", "REGISTRATION: Desregistrado OK.");
}
@Override
protected void onError(Context context, String errorId) {
    Log.d("GCMTest", "REGISTRATION: Error -> " + errorId);
}
Por último, en el método onMessage() procesaremos el intent con los datos recibidos en el mensaje y mostraremos una notificación en la barra de estado de Android. El intent recibido contendrá un elemento en su colección de extras por cada dato adicional que se haya incluido en la petición que hizo la aplicación servidor al enviar el mensaje. ¿Recordáis? Aquellos datos adicionales que había que preceder con el prefijo “data.”. Si hacéis memoria, en nuestros mensajes de ejemplo tan sólo incluíamos un dato llamado “data.msg” con un mensaje de prueba. Pues bien, estos datos se recuperarán de la colección de extras del intent llamado al método getString() con el nombre del dato, pero esta vez eliminando el prefijo “data.”. Veamos cómo quedaría todo esto:
1
2
3
4
5
6
@Override
protected void onMessage(Context context, Intent intent) {
    String msg = intent.getExtras().getString("msg");
    Log.d("GCMTest", "Mensaje: " + msg);
    mostrarNotificacion(context, msg);
}
Simple, ¿no?. Al final del método llamamos a un método auxiliar mostrarNotificacion() que será el encargado de mostrar la notificación en la barra de estado de Android. Esto también vimos como hacerlo en detalle en un artículo anterior por lo que tampoco comentaremos el código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private void mostrarNotificacion(Context context, String msg)
{
    //Obtenemos una referencia al servicio de notificaciones
    String ns = Context.NOTIFICATION_SERVICE;
    NotificationManager notManager =
        (NotificationManager) context.getSystemService(ns);
    //Configuramos la notificación
    int icono = android.R.drawable.stat_sys_warning;
    CharSequence textoEstado = "Alerta!";
    long hora = System.currentTimeMillis();
    Notification notif =
        new Notification(icono, textoEstado, hora);
    //Configuramos el Intent
    Context contexto = context.getApplicationContext();
    CharSequence titulo = "Nuevo Mensaje";
    CharSequence descripcion = msg;
    Intent notIntent = new Intent(contexto,
        GCMIntentService.class);
    PendingIntent contIntent = PendingIntent.getActivity(
        contexto, 0, notIntent, 0);
    notif.setLatestEventInfo(
        contexto, titulo, descripcion, contIntent);
    //AutoCancel: cuando se pulsa la notificaión ésta desaparece
    notif.flags |= Notification.FLAG_AUTO_CANCEL;
    //Enviar notificación
    notManager.notify(1, notif);
}
Y sólo una indicación más, además de sobrescribir estos métodos en nuestra clase GCMIntentService, también tendremos que añadir un nuevo constructor sin parámetros que llame directamente al constructor de la clase base pasándole de nuevo el Sender ID que obtuvimos al crear el nuevo proyecto en la Google API Console. Quedaría algo así:
1
2
3
public GCMIntentService() {
    super("224338875065");
}
Si no ha quedado claro del todo cómo quedaría la clase GCMIntentService completa puede descargarse y consultarse el código fuente completo al final del artículo.
Y con esto habríamos terminado de implementar nuestra aplicación Android capaz de recibir mensajes push desde nuestra aplicación web de ejemplo. Si ejecutamos ambas y todo ha ido bien, introducimos un nombre de usuario en la aplicación Android, pulsamos “Aceptar” para guardarlo, nos registramos como clientes en GCM pulsando el botón “Registrar GCM”, y seguidamente desde la aplicación web introducimos el mismo nombre de usuario del cliente, pulsamos el botón “Enviar GCM” y en breves segundos nos debería aparecer la notificación en la barra de estado de nuestro emulador como se observa en las imágenes siguientes:
Aplicación Ejemplo Android GCM
Aplicación Ejemplo Android GCM
Y si desplegamos la barra de estado veremos el mensaje de prueba recibido:
Notificación GCM Barra de Estado
Notificación GCM Barra de Estado
Como habéis podido comprobar en estos tres últimos artículos, la utilización de mensajes push requiere de un proceso algo laborioso pero para nada complicado. Os animo a que lo intentéis en vuestras aplicaciones ya que puede representar una característica interesante y útil.
Podéis descargar el código fuente del ejemplo construido en este artículo desde este enlace.
FUENTE: http://www.sgoliver.net/blog/?p=2879

lunes, 18 de febrero de 2013

GCM, Notificaciones Push en Android (COMPLETO)


Sin duda, si sos un programador medianamente avanzado,  te interesan las “Push Notifications” y/o has programado o tienes previsto programar Apps que hagan uso de esta forma de comunicación. Hablaremos de Notificaciones Push en Android, sumate, dale?


En esta ocasión presentamos la implementación de las “Push Notification” que han hecho los developers de Google y la adaptación a un entorno relativamente simple. Bastantes ideas hubo en los círculos de desarrolladores entorno a Google I/O 2012 que tuvo lugar los días 27 y 28 de Junio de 2012. En este evento fue donde presentaron la maduración de un proyecto bastante interesante llamado Google Cloud Messaging, maduración del ya antiguo C2DM.
Decir que con el sistema GCM podremos llevar a nuestras plataformas móviles los desarrollos propios que implementen o hagan uso de las llamadas “Push Notification” o “Mensajes Push”, es lo que algunos ya habran escuchado. Ahora está en boca de todos hablar sobre estos temas pero, exactamente ¿qué quiere decir notificaciones push?. Bueno no sabemos si exactamente o no, pero lo que debemos de tener muy claro es que es una forma de programar que se ideó justo cuando salieron al mercado los actuales gestores de correo. Es la forma en la que las Apps clientes son informadas sobre ciertas novedades o eventos evitando tener que consultar el estado del buzón de entrada de forma automática. Bueno ¿y para qué? En primer lugar ahorramos en la tarifa del pack de datos, ya que las consultas al servidor se harán de forma pasiva, solo se realizarán cuando el servidor tenga algo nuevo que decir; en segundo lugar, y como es lógico pensar, ahorraremos en el consumo de batería, ya que se hace un uso optimizado de los recursos del teléfono.
WhatsApp, Google Talk, Facebook Messenger? Si usas alguno de esos, seguro que sabes que tienen algo en común. Y si, el sistema de comunicaciones se hace a través de Notificaciones Push. Vamos a repasar este tema y ver como funcionan, desarrollando Apps en Android.


Ya hace bastante tiempo que estas aplicaciones se han instalado en nuestras vidas y que han conseguido desbancar casi por completo a los obsoletos SMS. Ahora gracias al nuevo servicio de Google Cloud Messaging, podemos crear nuestro propio sistema de notificaciones Push en nuestras aplicaciones. Pensá como puede llegar a mejorar nuestra aplicación con un sencillo servicio de notificaciones, la experiencia del usuario cambia bsatante, en aplicaciones puntualmente enfocadas Conferencias, Agendas de Eventos que utilizando la API de Calendar (si, utilizando y configuracion el calendario del usuario en su Android).

Voy a tratar de explicar brevemente como sería el proceso de envío. En el primer paso nuestro dispositivo se registra de forma totalmente transparente al usuario en el sevidor de Google para decirle que puede recibir notificaciones para nuestra aplicación. En este registro el servidor de Google nos devuelve un Identificador a nuestra aplicación Android que deberemos almacenar en un servidor de nuestra propiedad. Una vez ya están todos presentados, desde nuestro servidor ya se pueden empezar a enviar mensajes. Para esto se envían pedidos al servidor de Google y este se encarga de enviarlo si el dispositivo está online o de esperar a que el dispositivo se reconecte para enviarlo.
Toda la informacion que aqui vamos a compartir es una recopilacion de distintas paginas webs de referencia.

Requisitos previos a la práctica

El software que deberá tener instalado el desarrollador para llevar a cabo esta práctica es:
  •  JDK (1.6 )
  • Tomcat (6)
  • Plataforma de desarrollo Android (SDK, Eclipse for Java EE Developers y plugin ADT para eclipse )
 Además de los requisitos de software, en cuya instalación no nos detendremos, antes de comenzar a desarrollar también debemos de: 
  • Disponer de una cuenta gratuita en los servicios de Google APIs y registrar el nuevo proyecto.
  • Disponer de las librerías necesarias para manipular el API GCM.
Lo primero que debemos hacer es activar el servicio de Google Cloud Messaging en la siguiente dirección https://code.google.com/apis/console. Tenes que disponer de una cuenta de Google para poder realizar este proceso.




Creamos un proyecto el cual nos dará un senderId que deberemos utilizar en la app Android. Lo pdemos encontrar en la pestaña Overview, sección Project Number.

Lo siguiente es activar el servicio de mensajería. Para esto en la pestaña Services buscá Google Cloud Messaging for Android y lo pones a ON. Ya por último, vas a API Access y pulsas el botón "Create new Server key" (en la pantalla que aparece no hace falta rellenar nada). Esto nos creará una apiKey, ojo, la de la sección Key for server apps. La cual utilizaremos después.

Esta es toda la configuración que necesitamos en el servidor de Google.

Vamos ahora a crear la parte servidor. Para ello haremos como es habitual en PHP y MySQL, pero lo podes adaptar de una forma muy simple al lenguaje que necesites.

Lo primero que vamos a hacer es crear una tabla en nuestra base de datos para almacenar los usuarios que se registren en nuestra app para saber los registration id que nos proporcionó Google Cloud Messaging.


CREATE TABLE IF NOT EXISTS `usuarios` (
  `username` varchar(100) NOT NULL,
  `gcmcode` varchar(500) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB;


Como podés ver, es una tabla de usuarios muy simple. El nombre de usuario es clave pero el gcmcode no, porque de vez en cuando el servicio de Google puede cambiarlo y nosotros debemos actualizarlo.

Vamos a crear 3 archivos php, uno para gestionar las llamadas a las funciones de nuestra aplicación servidor, otro para los usuarios y otro para los mensajes. Vamos a empezar por los usuarios. Para esto creamos un archivo llamado users.php, asi:


<?php
class users {
 
 function __construct() {
  $con = mysql_connect("NOMBRE_HOST", "NOMBRE_DB_USER", "DB_PASSWORD");  
  mysql_select_db("NOMBRE_DB_DATABASE");
 }
 
 public function saveUser($username, $gcmcode){
  $response = array("success" => "0");
  $sql  ="INSERT INTO usuarios (username, gcmcode) VALUES ('" . stripslashes($username) . "', '" . stripslashes($gcmcode) . "') 
    ON DUPLICATE KEY UPDATE gcmcode = '" . stripslashes($gcmcode)."' ; ";
  
  $result = mysql_query($sql);
  if(mysql_affected_rows() > 0)
   $response["success"] = "1";  
  
  return json_encode($response);
 }
 
 public function getUser($username){
  $response = array("success" => 0);
  
  $sql = "SELECT username, gcmcode
    FROM usuarios 
    WHERE username='".stripslashes($username)."';";
  $result = mysql_query($sql);
  if(mysql_num_rows($result) > 0){
   $data = mysql_fetch_array($result);
   
   return $data;
  } else 
   return false;  
 }
}
?>



Para empezar, en el constructor de la clase users configuramos la base de datos. Se debería tener los parámetros en un archivo de configuración a parte, pero para simplificar lo dejamos así. El método saveUser recibe el nombre de nuestro usuario en la aplicación y el código que nos dió Google. Como ven se intenta hacer un insert en la tabla de usuarios y en caso de que el usuario ya exista se renueva el código de Google. Esto es por lo que comentaba antes, Google de vez en cuando puede cambiar nuestro código usuario y debemos actualizarlo. El método getUser lo utilizaremos en el siguiente archivo y nos devolverá información sobre un determinado usuario.

Lo siguiente es el archivo destinado a gestionar los mensajes, el cual llamaremos gcm.php


<?php 
class gcm {
 
 function __construct() {
  $con = mysql_connect("NOMBRE_HOST", "NOMBRE_DB_USER", "DB_PASSWORD");
  mysql_select_db("NOMBRE_DB_DATABASE");
 }
 
 function sendMessageToPhone($collapseKey, $messageText, $username)
 {
  include_once 'users.php';
  $user = new users();
  $data = $user->getUser($username); 
  if($data != false){
  
   $apiKey = 'AQUI LA API KEY PARA SERVER QUE NOS DIO GOOGLE';
   
   $userIdentificador = $data["gcmcode"];
   
   $headers = array('Authorization:key=' . $apiKey);
   $data = array(
     'registration_id' => $userIdentificador,
     'collapse_key' => $collapseKey,
     'data.message' => $messageText);
  
   $ch = curl_init();
  
   curl_setopt($ch, CURLOPT_URL, "https://android.googleapis.com/gcm/send");
   if ($headers)
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
   curl_setopt($ch, CURLOPT_POST, true);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
   curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  
   $response = curl_exec($ch);
   $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
   if (curl_errno($ch)) {
    return 'fail';
   }
   if ($httpCode != 200) {
    return 'status code 200';
   }
   curl_close($ch);
   return $response;
  } else {
   return 'no user';
  }
 } 
}

?>

Otra vez en el constructor estamos inicializando la conexión a la base de datos. Y el método sendMessagetoPhone recibe tres parámetros. El primero es un identificador para indicarle a Google si debe colapsar o no los mensajes en caso de que hablen de un mismo tema. Si lanzas notificaciones sobre una misma noticia por ejemplo, colapsará los mensajes. Podes utilizar por ejemplo el id de la noticia, su permalink que actuará como identificador, en fin, algo único que sirve para identificar notificaciones del mismo tipo. El segundo es el mensaje que vamos a mandar y el tercero el nombre del usuario al que le enviamos el mensaje.

Este método lo que va a hacer es enviar al servidor de Google una petición para enviar un mensaje. En el apartado de registration_id, si queremos enviar a más de un dispositivo simplemente creamos un array con los ids de los usuarios de destino. Y el atributo data.message nos sirve para enviar el texto del mensaje. Si quisieramos enviar más datos como por ejemplo la fecha, añadiriamos el parámetro data.fecha, y si queremos, también el nombre del autor sería data.autor. Todo lo que querramos añadir a mayores debe ir precedido de "data.".

Finalmente este método nos devolverá el resultado de la petición, la cual puede fallar por diversos motivos. El como gestionemos los errores ya dependerá de cada uno.

El último archivo que nos queda por ver es el index.php, este servirá como entrada para llamar a todas los métodos que creamos antes.


<?php
include_once 'users.php';
include_once 'gcm.php';
$tag = $_POST['tag'];

switch ($tag){
 case 'usersave':
  $username = "";
  $gcmcode = "";
  if(isset($_POST['username']))
   $username = $_POST['username'];
  if(isset($_POST['gcmcode']))
   $gcmcode = $_POST['gcmcode'];
  
  $user = new users();
  echo $user->saveUser($username, $gcmcode);
  break;
 case 'sendmessage':
  $username = "";
  $message = "";
  $collapseKey = "";
  if(isset($_POST['message']))
   $message = $_POST['message'];
  if(isset($_POST['username']))
   $username = $_POST['username'];
  if(isset($_POST['collapsekey']))
   $collapseKey = $_POST['collapsekey'];
  $message = new gcm();
  echo $message->sendMessageToPhone($collapseKey, $message, $username);
  break;
 default:
  $response = array("success" => "0", "err" => "no tag");
  echo json_encode($response);
  break;
}
?>



El archivo es muy simple, simplemente recibe por post un parámetro tag que será el que nos diga que acción realizar. Si insertamos o actualizamos un nuevo usuario además debemos enviar el nombre de usuario y el código de registro para ese usuario en el servicio de Google.

Si lo que hacemos es enviar un mensaje le indicamos el nombre de usuario, el mensaje y el collapseKey que será por así decirlo como un identificador para mensajes que se agrupen por un mismo tema. Por ejemplo para una noticia en concreto. En este caso estamos asumiendo que solo vamos a enviar a un único usuario, pero si fueramos a enviar a más usuarios, deberiamos modificar el método para poder recibir todos los usuarios que quisieramos.

Bueno, ahora veremos la parte cliente en Android. Como registrar un usuario en el servicio de Google Cloud Messaging y como recibir mensajes y notificarlo.  Daremos de alta y baja a un usuario y prepararemos nuestra aplicación para recibir mensajes.


Como antes, debemos comprobar si tenemos instalado en nuestro SDK de Android la librería gcm.jar que nos facilitará casi todo el trabajo. Para ello, en Eclipse vamos a Window > Android SDK Manager. Aquí nos aparecerán todas las APIs y librerías que tenemos instaladas. Vamos a la sección Extras y si no está instalada la opción Google Cloud Messaging for Android Library la chequeamos e instalamos.




Una vez terminada la instalación, vamos a la carpeta donde tengamos instalado el SDK de Android y desde ahí hasta la carpeta \extras\google\gcm\gcm-client\dist y copiamos el archivo gcm.jar.

Ahora vamos a nuestro proyecto y en la carpeta libs pegamos este archivo. Hacemos click con el botón derecho del mouse sobre él y seleccionamos Build Path > Add to Build Path. Ahora ya podemos empezar a crear nuestro servicio para recibir notificaciones push.

Empezaremos primero por el archivo AndroidManifest.xml :


<permission android:name="nombre.del.package.de.nuestra.app.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="nombre.del.package.de.nuestra.app.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE"/>
<application>
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver"
         android:permission="com.google.android.c2dm.permission.SEND" >
         <intent-filter>
             <action android:name="com.google.android.c2dm.intent.RECEIVE" />
             <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
             <category android:name="nombre.del.package.de.nuestra.app" />
         </intent-filter>
     </receiver>
  
     <service android:name=".GCMIntentService" />
</application>



Las primeras líneas son para indicarle al dispositivo que podrá recibir notificaciones del servicio de Google Cloud Messaging, además indica que solo nuestra aplicación (la que tiene nuestro package) podrá hacerlo. El siguiente permiso, GET_ACCOUNTS lo necesita Google para su servicio de mensajería. Después vienen permisos para internet y verificar el estado de la red, imprescindibles para un servicio de estas características. WAKE_LOCK permitirá "despertar" nuestro dispositivo si está "durmiendo". Y el último VIBRATE, es opcional, simplemente lo usareis si queréis que al notificar algo nuestro dispositivo vibre.

Después, dentro de la etiqueta application (miren que a esta etiqueta no le he puesto ningún atributo, ustedes dejen la que tengan en su app, simplemente es para que sepan donde poner lo siguiente) definimos un BroadcastReceiver el cual ya viene definido en la librería gcm.jar y hará todo el trabajo sucio, registrar nuestro usuario, recibir notificaciones. Únicamente debemos respetar el nombre del broadcast.

Por último, se define un Service que crearemos luego y que nos ayudará cuando registremos un usuario, cuando recibamos un mensaje o tengamos algún error.

Ahora que ya tenemos el AndroidManifest.xml aclarado vamos a ver como se registran y desregistran los usuarios en nuestra app. El método registerUser antes de nada verifica si existe en el dispositivo el registrarion id que nos da el servicio de Google Cloud Messaging para cada usuario. Si no existe ese código lo registramos con la ayuda de la librería gcm.jar, y si existe no hacemos nada, evitando el proceso de llamar al servicio de Google.

El proceso de desregistro es muy similar, verificamos si existe el código de registro de usuario y llamamos al método unregister de la librería gmc.jar.


private void registerUser(Context context){
  
        final String regId = GCMRegistrar.getRegistrationId(context);
        if (regId.equals("")) {
         GCMRegistrar.register(context, "SENDER_ID QUE VIMOS ANTERIORMENTE"); 
         Log.v("GCM", "Registrado");
        } else {
            Log.v("GCM", "Ya registrado");
        } 
 }
private void unregisterUser(Context context){
 final String regId = GCMRegistrar.getRegistrationId(context);
        if (!regId.equals("")) {
            GCMRegistrar.unregister(context);
        } else {
            Log.v("GCM", "Ya des-registrado");
        }
 }

Los dos métodos los pongo solos y no los ubico en ninguna activity porque ustedes deben decidir donde van. Por ejemplo, si la app no tiene login podrian poner el método de registro en el onCreate de la primera activity y así todos los usuarios recibirían notificaciones. Si tiene login se puede poner una vez el usuario se haya logado. O a lo mejor solo quieren activarlo y desactivarlo en un formulario donde el usuario lo indique expresamente, mediante las SharedPreference, por ejemplo.

El último paso que nos queda por realizar es crear el Service que comentamos antes en el archivo AndroidManifest.xml, para esto creamos una nueva clase llamada GCMIntentService y que heredará de GCMBaseIntentService.


public class GCMIntentService extends GCMBaseIntentService {

 public GCMIntentService(){
  super("SENDER ID QUE NOS DIO LA API DE GOOGLE");
 }
 
 @Override
 protected void onError(Context context, String errorId) {
  Log.d("GCM", "Error: " + errorId);
 }

 @Override
 protected void onMessage(Context context, Intent intent) {
     String msg = intent.getExtras().getString("msg");
     Log.d("GCM", "Mensaje: " + msg);
     notificarMensaje(context, msg);
 }

 @Override
 protected void onRegistered(Context context, String regId) {
  Log.d("GCM", "onRegistered: Registrado OK.");
   //En este punto tenemos que obtener el usuario donde lo tengas guardado.
   //Si no tenes un sistema de login y los usuarios son anónimos podes simplemente almacenar el regId
  String usuario = "";
  
    registrarUsuario(usuario, regId);

 }

 @Override
 protected void onUnregistered(Context context, String regId) {
  Log.d("GCM", "onUnregistered: Desregistrado OK.");
 }
 
 private void registrarUsuario(String username, String regId){
  Log.w("GCM", "registrarUsuario");
  Log.w("GCM", "username: " + username);
  Log.w("GCM", "regID: " + regId);
  try{
   
   ArrayList<namevaluepair> nameValuePairs = new ArrayList<namevaluepair>();
   nameValuePairs.add(new BasicNameValuePair("tag","usersave"));
   nameValuePairs.add(new BasicNameValuePair("username",username));
   nameValuePairs.add(new BasicNameValuePair("gcmcode",regId));
   
   HttpClient httpclient = new DefaultHttpClient();
   HttpPost httppost = new HttpPost("http://ip donde está alojado el servicio web/index.php");
   if(nameValuePairs != null)
    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
   
   ResponseHandler<string> responseHandler = new BasicResponseHandler();
   String res = httpclient.execute(httppost, responseHandler);
   
   Log.w("GCM", "RES: " + res);
   

  } catch(Exception e){
   Log.w("GCM", "ex: " + e.getMessage().toString());
  }
 }
 
 private void notificarMensaje(Context context, String msg)
 {
     String notificationService = Context.NOTIFICATION_SERVICE;
     NotificationManager notificationManager =(NotificationManager) context.getSystemService(notificationService);
  
     //Configuramos la notificación
     int icono = android.R.drawable.ic_launcher;
     CharSequence estado = "Has recibido un nuevo mensaje";
     long hora = System.currentTimeMillis();
  
     Notification notification = new Notification(icono, estado, hora);
     long[] vibrate = {100,100,200,300};
     notification.vibrate = vibrate;
  
     //Configuramos el Intent
     Context contexto = context.getApplicationContext();
     CharSequence titulo = "Nombre app - nuevo Mensaje";
     CharSequence descripcion = msg;
  
     Intent intent = new Intent(contexto, activitydestino.class);
     Bundle b = new Bundle();
     b.putInt("update", 1);
     intent.putExtra("android.intent.extra.INTENT", b);

     PendingIntent contIntent = PendingIntent.getActivity(contexto, 0, intent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
  
    notifification.setLatestEventInfo(contexto, titulo, descripcion, contIntent);
  
    notification.flags |= Notification.FLAG_AUTO_CANCEL;
  
    notificationManager.notify(1, notification); }

}


Esta última parte es un poco más compleja, pero no mucho. Lo primero es mirar en el constructor, debe llamar al constructor padre pasándole el sender id que nos da Google, lo que en la web es el ID Project.

Despues tendremos cuatro eventos básicos que sobreescribimos para hacer lo que más se adapte a nuestra aplicación. Tenemos onError, que nos informa de algún error en el proceso. onRegistered y onUnregistered que se producen cuando un usuario se da de alta o baja del servicio de Google Cloud Messaging, en estos dos casos debemos dar de alta o baja al usuario en nuestro servidor. Y por último, onMessage, que se produce cada vez que llega un mensaje push.

Lo que hace el método registrarUsuario ya lo hemos visto otras veces, simplemente es una llamada al servicio web que hicimos y que envía el nombre de usuario, el código de Google Cloud Messaging y un tag para indicar la acción a realizar. A la hora de tomar el resultado de la acción no hacemos nada con el resultado, pero se podría o bien guardar en caso de error el usuario para volver a intentar registrar más tarde o bien des-registrar del servicio de Google al usuario y que al volver a entrar se repitiera el proceso de registro. El como, cuando y donde hacer esta parte ya les corresponde a ustedes y como hayan planteado el alta del usuario en el sistema.

El último método notificarMensaje simplemente recibe un mensaje y lo notifica al usuario. Si recordas, en el servicio web se mandaban parámetros con data.msg, data.fecha, data.autor... para tomarlos, en onMessage se utiliza: intent.getExtras().getString("msg"); Esto será para el caso de data.msg, y haces lo mismo para el resto sustituyendo msg por lo que corresponda.

A la hora de notificar hacemos uso de notificationManager, podemos configurar el icono, el texto de cabecera, le pasamos la descripción que será el mensaje que hemos recibido. Si queremos podemos hacer que vibre el teléfono. También podemos definir un Intent para que al pulsar en el mensaje abra la aplicación en un determinado punto y además en nuestro caso le pasamos un parámetro que en mi caso se llama update y su valor es 1, de esta forma puedo ahorrarle mucha batería y tráfico de datos al usuario si solo actualizo datos cuando yo se lo notifico.

Se podría enviar en los parámetros datos como un identificador y abrir la ficha de una noticia por ejemplo, o lo que a ustedes se les ocurra. Incluso desde el notificarMensaje podes hacer de forma totalmente silenciosa alguna actualización de datos y el usuario al entrar no tendría que esperar y siempre tendría los datos actualizados.

Por último, para empezar a enviar notificaciones para recibirlas en el dispositivo móvil, debes utilizar el servicio web que se presentó al comienzo, haciendo llamadas al método sendMessageToPhone de la clase gcm. Este lo podrías integrar en una web, por ejemplo cada vez que se publique una noticia que se llame. También en un chat, o incluso podes hacer llamadas a este servicio desde la misma aplicación móvil para tener nuestra app de mensajería.

Listo por el momento, les recuerdo que siempre pueden consultar el codigo de referencia que tenemos al descargarnos el SDK de Android, ahi tenemos un cliente y un servidor para poder mirar.

FUENTE: http://www.nosinmiubuntu.com/2012/12/notificaciones-push-en-android-ii.html 

viernes, 15 de febrero de 2013

La clave del éxito: ser como un bambú japonés

Encontré este genial texto en un interesante blog:  http://porsilasmoscasblog.wordpress.com  , los invito a navegar ese blog, y leer un poco.
Por las dudas, yo les comparto a continuacion el mejor de los texto, para continuar con este duro mes de Febrero 2013, y luego retomar con todas las pilas, las distintas notas y novedades sobre Android.


La clave del éxito: ser como un bambú japonés

9ENE
“Algunas personas se ahogan en un vaso de agua, otras se toman el agua y venden el vaso”, escribió un tal Jonathan Moldú y otra vez la inspiración se apoderó de mí. Incluso me dieron ganas de especular con el precio que le pondría al vaso. ¡Cosa de locos!
Lo cierto es que a veces estamos hundidos, agobiados, sin fuerzas para pegar una patada o tomarnos del borde, cual pileta de natación, y respirar nuevos aires…
Hasta los risueños sentimos pánico y queremos tirar la toalla, lo que sucede es que lo vestimos de fiesta y salimos a relucir nuestra mejor sonrisa que, aunque espantada de la farsa, pocas veces nos delata.
Una maestra siempre nos contaba la historia de las ranas que se cayeron dentro de un recipiente con crema. Allí, mientras una se dejaba morir, tapada por la grasa, la otra -tozuda y persistente- pataleaba hasta transformarla en manteca y escapaba.
Todavía tengo la sensación de estar escuchando el relato. Me imagino sentada en mi pupitre destartalado, rodeada de cortinas blancas, larguísimas, que colgaban de los ventanales inmensos del aula. Aún puedo sentir el orgullo que me produjo aquella rana insolente y obstinada.
Ya en la facultad, un profesor logró borra mi admiración por el anfibio y me hizo conmover ante la grandeza del bambú japonés, no apto para impacientes.
La cuestión es que, luego de siete años de sembrar la semilla de este árbol, todo permanece, a simple vista, como el primer día y cualquiera podría jurar que la operación fue un fracaso.
Sin embargo, durante el séptimo año, en tan solo seis semanas, la planta de bambú crece ¡más de 30 metros!
Mientras todos lo aguardan ansiosos, el muy pícaro se toma un buen tiempo para desarrollarse, generando un complejo sistema de raíces que le permitirán sostener el crecimiento que tendrá luego.
Hoy, seguramente, haya quienes estén cansados de luchar, a punto de dejar el sable y la armadura, listos para darse por vencidos aún cuando no ha comenzado la batalla. Pero quizá, lo mejor es comprender que el éxito es simplemente resultado del crecimiento interno y que éste requiere tiempo.
De nada sirve apurarse, tampoco obtener resultados rápidos que no podrán sostenerse a lo largo de nuestras vidas. ¡Sí, señores, se los dice la misma que a veces no puede aguardar para cambiar de calendario y los lunes planifica lo que hará el fin de semana!
Lo que sucede es que entendí que hay que saber esperar el momento;  que aunque a veces creamos que nada está ocurriendo puede que, en secreto, estemos echando raíces…