En esta entrada veremos cómo crear una notificación personalizada con Android empleando como ambiente de desarrollo de software Android Studio.
¿A qué nos referimos con notificación personalizada?
A que emplearemos una vista desarrollada desde cero para tal fin; incluso emplearemos una serie de botones los cuales pueden ejercer sus acciones sobre la actividad que crea dicha notificación, servicios o Broadcast que definieramos.
Creando la notificación personalizada
Los intent en el paso de datos
El código de la vista está un poco más adelante en la entrada, pero por ahora indicaremos los primeros pasos para crear una notificación en Android.
Intent intent = new Intent(getApplicationContext(), MusicService.class); noBundle.putInt("accion", 1);//This is the value I want to pass intent.putExtras(noBundle); PendingIntent pendingIntent = PendingIntent.getService(ListActivity.this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT); view.setOnClickPendingIntent(R.id.ibAccion, pendingIntent);
Expliquemos el propósito de cada componente del código presentado anteriormente:
- El
Bundle
Es empleado generalmente para pasar datos entre componentes como actividades y fragments a través de números, textos o booleanos. - El
Intent
es el componente empleado para iniciar la comunicación entre los componentes como inicar una actividad o servicio (más información en la documentación oficial). - El
PendingIntent
es un objeto que contiene a un intent; este objetoPendingIntent
permite alNotificationManager
mandar al sistema la acción a ejecutar (como abrir una actividad).
Muy importante la bandera PendingIntent.FLAG_UPDATE_CURRENT
que en conjunto con:
<activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:launchMode="singleTop" android:screenOrientation="portrait"> ... </activity>
Indica que la actividad existe actualmente (por defecto la MainActivity es la primera actividad que inició) y el android:launchMode="singleTop"
y es una forma de indicar que la instancia de la actividad sea única, aunque existen otras formas como puedes consultar en el siguiente enlace (en otras palabras significa actualizar la instancia de la actividad que está en ejecución actualmente).
Definiendo las acciones de la vista personalizada
Cómo son tres botones, necesitamos tres Intent
s diferentes y todo lo que esto conlleva, por lo tanto ahora multiplicamos esto por tres:
Intent intent = new Intent(getApplicationContext(), MusicService.class);
noBundle.putInt("accion", 1);//This is the value I want to pass
intent.putExtras(noBundle);
PendingIntent pendingIntent = PendingIntent.getService(ListActivity.this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibAccion, pendingIntent);
Bundle noBundle2 = new Bundle();
noBundle2.putInt("accion", 2);//This is the value I want to pass
Intent intent2 = new Intent(getApplicationContext(), ListActivity.class);
intent2.putExtras(noBundle2);
PendingIntent pendingIntent2 = PendingIntent.getActivity(ListActivity.this, 2, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibQuitar, pendingIntent2);
Bundle noBundle3 = new Bundle();
noBundle3.putInt("accion", 3);//This is the value I want to pass
Intent intent3 = new Intent(getApplicationContext(), ListActivity.class);
intent3.putExtras(noBundle3);
PendingIntent pendingIntent3 = PendingIntent.getActivity(ListActivity.this, 3, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibSiguiente, pendingIntent3);
Lanzando la notificación personalizada
Ahora podemos crear nuestra notificación; primero el administrador de la notificación (NotificationManager
) que como su nombre indica y mediante una instancia de esta clase podemos lanzar la notificación con el NotificationCompat.Builde
podemos establecer las propiedades de la notificación y crear la notificación propiamente dicha; por ejemplo indicamos el título, subtítulo el icono y el PendingIntent
cuya acción será ejercida al momento de que el usuario realice un clic sobre la notificación:
NotificationManager nManager;
NotificationCompat.Builder nBuilder;
RemoteViews remoteView;
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, ListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
nBuilder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setTicker(getString(R.string.app_name))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(contentIntent)
.setOngoing(false)
.setAutoCancel(false);
Como podemos apreciar, al momento de crear la notificación establecemos varios parámetros como el contenido (título), icono y el mismo Intent
que definimos anteriormente.
Ahora falta especificar cual es la vista en cuestión (la que posee el diseño que creamos con los tres botones), esto lo hacemos mediante un RemoteViews
:
remoteView = new RemoteViews(getPackageName(), R.layout.notification_layout);
remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
remoteView.setTextViewText(R.id.title, "Título");
remoteView.setTextViewText(R.id.text,"Nombre");
Como podemos ver en el código anterior, accedemos a cada una de sus componentes y establecemos un valor que para efectos de este tutorial son fijos.
En nuestro caso la vista tiene el siguiente contenido:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#333333"
android:orientation="vertical"
android:weightSum="1">
<LinearLayout
android:id="@+id/right"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/imagenotileft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@mipmap/ic_launcher" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="9dp"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:textColor="#EEEEEE"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<ImageButton
android:id="@+id/ibAtras"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:background="@NULL"
android:gravity="center_horizontal|center_vertical"
android:padding="0dp"
android:src="@drawable/ic_action_av_skip_previous" />
<ImageButton
android:id="@+id/ibAccion"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:background="@NULL"
android:gravity="center_horizontal|center_vertical"
android:padding="0dp"
android:src="@drawable/ic_action_ic_play_pause" />
<ImageButton
android:id="@+id/ibSiguiente"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:background="@NULL"
android:gravity="center_horizontal|center_vertical"
android:padding="0dp"
android:src="@drawable/ic_action_av_skip_next" />
<Button
android:id="@+id/ibQuitar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:background="@NULL"
android:gravity="center_horizontal|center_vertical"
android:padding="0dp"
android:text="X"
android:textColor="#444444"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
Finalmente establecemos la vista anterior y lanzamos la notificación:
setListeners(remoteView);
nBuilder.setContent(remoteView);
nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(2, nBuilder.build());
Replicar las notificaciones en Android
Hasta ahora hemos visto cómo crear una notificación con un layout personalizado y cómo interactuar con los elementos que definimos en este layout como Buttons
o ImageView
, etc; en esta segunda parte de esta entrada veremos cómo crear notificaciones que se puedan replicar; esta es una acción muy empleada en aplicaciones de mensajería como Hangout de Google o el mismo Whatsapp en Android.
Compatibilidad entre las notificaciones
Las notificaciones tienen una gran compatibilidad hacia atrás empleando la clase NotificationCompat.Builder y podemos emplear muchisimas opciones que tienen desde prioridad hasta estilos pero debemos tener muy presentes que funcionalidades empleamos ya que algunas funciones en específico están provistas solo para versiones superiores de Android superiores a la 4.1.
Creando el medio para el pase de datos entre componentes: PendingIntent
Lo primero que debemos hacer es crear un PendingIntent
y asociar el Intent
que es el medio empleado para pasar datos de un componente; estos componentes no solo pueden ser Actividades o Fragments, también pueden ser otros como Broadcast para realizar actualizaciones en segundo plano sin la necesidad de invocar a la actividad; aunque para esto debemos emplear Android N en adelante; para no extender demasiada esta entrada, nos iremos por el caso en que se invoca a la actividad.
El PendingIntent
es el mecanismo existente que tenemos para pasar datos entre distintos componentes como actividades, broadcast o servicios que embebe dentro del mismo los datos que queremos pasar a estos componentes (en caso de que se desee pasar un dato):
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
.setLabel(replyLabel)
.build();
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setAction(KEY_TEXT_REPLY);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent replyPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
El método setAction
permite asociar el nombre del dato a pasar, que en este caso será el texto que el usuario suministre en el campo de réplica y con este nombre accedemos al texto que ingreso el usuario en el onCreate
de nuestra actividad:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
if (KEY_TEXT_REPLY.equals(intent.getAction())) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != NULL) {
Log.i("REPLICA", "" + remoteInput.getCharSequence(KEY_TEXT_REPLY));
}
}
replyNotification();
}
Con el método RemoteInput
indicamos que crearemos un mecanismo para el pase de datos dentro del PendingIntent
; además de asociamos el label o texto que va a aparecer en el campo de la réplica.
Creando la notificación replica
Asociamos el PendingIntent
que creamos anteriormente:
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(R.mipmap.ic_launcher,
"Replicar", replyPendingIntent)
.addRemoteInput(remoteInput)
.build();
Creando la notificación replica
Creamos la notificación estableciendo el contenido de la misma: icono representativo, título, contenido y la acción que configuramos en el bloque anterior:
NotificationCompat.Builder newMessageNotification =
new NotificationCompat.Builder(MainActivity.this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My notification")
.setContentText("Hello World!")
.addAction(action);
Finalmente lanzamos la notificación junto con las acción que configuramos en los pasos anteriores a través del método build()
:
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(MainActivity.this);
notificationManager.notify(NOTIFICATION_ID, newMessageNotification.build());
El método completo queda como:
public void replyNotification() {
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
.setLabel(replyLabel)
.build();
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setAction(KEY_TEXT_REPLY);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent replyPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(R.mipmap.ic_launcher,
"Replicar", replyPendingIntent)
.addRemoteInput(remoteInput)
.build();
NotificationCompat.Builder newMessageNotification =
new NotificationCompat.Builder(MainActivity.this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My notification")
.setContentText("Hello World!")
.addAction(action);
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(MainActivity.this);
notificationManager.notify(NOTIFICATION_ID, newMessageNotification.build());
}
Finalmente el resultado es el siguiente:
Enlaces de referencia:
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter