Notificaciones Push en Android II

La semana pasada le echamos un vistazo a las notificaciones push en Android de la mano del servicio Google Cloud Messaging y preparamos la parte servidor. Esta semana vamos a completar lo anterior preparando la parte cliente. Daremos de alta y baja a un usuario y prepararemos nuestra aplicación para recibir mensajes.


Como la semana pasada, antes de meternos en harina, 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 ratón 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 AndroidManifext.xml que hoy tiene más código del habitual.


<permission android:name="nombre.del.package.de.vuestra.app.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="nombre.del.package.de.vuestra.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.vuestra.app" />
         </intent-filter>
     </receiver>
  
     <service android:name=".GCMIntentService" />
</application>



Como veis es un poco más completo de lo habitual. 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 vuestro 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 vuestro dispositivo vibre.

Después, dentro de la etiqueta application (fijaros que a esta etiqueta no le he puesto ningún atributo, vosotros dejad los que tengáis, simplemente es para que sepáis 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 debéis 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 EN LA ENTRADA ANTERIOR"); 
         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 debéis ser vosotros los que decidáis donde van. Por ejemplo, si vuestra app no tiene login podríais 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 queréis activarlo y desactivarlo en un formulario donde el usuario lo indique expresamente.

El último paso que nos queda por realizar es crear el Service que comentamos antes en el archivo AndroidManifest.xml, para ello 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 debeis obtener el usuario donde lo tengais guardado.
   //Si no teneis un sistema de login y los usuarios son anónimos podeis 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 fijaros en el constructor, debe llamar al contructor 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 más veces, simplemente es una llamada al servicio web que hicimos la semana pasada 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 recoger 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 os corresponde a vosotros y como hayáis planteado el alta del usuario en vuestro sistema.

El último método notificarMensaje simplemente recibe un mensaje y lo notifica al usuario. Si recordáis, en el servicio web se mandaban parámetros con data.msg, data.fecha, data.autor... para recogerlos, en onMessage se utiliza: intent.getExtras().getString("msg"); Esto será para el caso de data.msg, y haceis 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 vosotros se os ocurra. Incluso desde el notificarMensaje podéis 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, debéis utilizar el servicio web de la semana pasada haciendo llamadas al método sendMessageToPhone de la clase gcm. Este lo podríais integrar en una web, por ejemplo cada vez que se publique una noticia que se llame. También en un chat, o incluso podéis hacer llamadas a este servicio desde la misma aplicación móvil para tener vuestra app de mensajería.

Si queréis podéis aprender más sobre como trabajar en vuestra aplicación con acciones en el artículo:  enviar y recibir acciones con BroadcastReceiver en Android.

Comments are closed.