domingo, 31 de outubro de 2010

Notificações personalizadas no Android

Olá povo,

A barra de notificações do Android permite chamar a atenção do usuário sem interromper o que ele possa estar fazendo. Por padrão, criamos uma notificação que exibe um texto quando ela é disparada, e outros dois quando a barra de notificações é expandida. Para tal, podemos executar o código abaixo:
// Cria a intent que será excutada ao clicar na notificação
Intent it = new Intent("UMA_ACTIVITY");
PendingIntent pi = 
  PendingIntent.getActivity(context, 0, it, 0);
   
// Criando Notificação
Notification notificacao = new Notification();
notificacao.icon = R.drawable.icon;
notificacao.tickerText = "Chegou uma notificação";
notificacao.when = System.currentTimeMillis();
notificacao.flags = Notification.FLAG_AUTO_CANCEL;

notificacao.setLatestEventInfo(context,
  "Mensagem Superior", "Detalhes", pi);
   
NotificationManager nm = (NotificationManager)
  context.getSystemService(
    Context.NOTIFICATION_SERVICE);
nm.notify(1234, notificacao);

Com isso teremos o resultado abaixo:

Porém, se quisermos personalizar a notificação podemos faze-lo criando um arquivo de layout e definindo-o como view da notificação. Vou exemplificar isso utilizando um exemplo bem comum: barra de progresso. Quando baixamos um arquivo do browser ou do Android market é possível ver a barra de notificação exibindo uma barra de progresso. Segue abaixo o código que faz isso.
Arquivo res/layout/minha_notificao.xml
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout_id"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:padding="3dp">
 
<ImageView
  android:src="@drawable/icon"
  android:id="@+id/icone"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginRight="10dp"/>

<ProgressBar
  android:id="@+id/barraDeProgresso"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_toRightOf="@+id/icone"
  style="@android:style/Widget.ProgressBar.Horizontal" />
 
<TextView
  android:id="@+id/textoDeProgresso"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_toRightOf="@+id/icone"
  android:layout_below="@+id/barraDeProgresso"/>
</RelativeLayout>

Agora vejamos como criar a notificação personalizada:
Intent it = new Intent("UMA_ACTIVITY");
PendingIntent pi = 
  PendingIntent.getActivity(this, 0, it, 0);
    
final Notification notificacao = new Notification();
notificacao.icon = R.drawable.icon;
// Não usaremos setLatestEventInfo, ao invés
// definimos a pending intent da notificação
notificacao.contentIntent = pi;

// Carrega as views a partir do arquivo de layout
final RemoteViews contentView = new RemoteViews(
  getPackageName(), R.layout.minha_notificacao);

// Define as views remotas como view da notificação
notificacao.contentView = contentView;

// Obtém a referência do NotificationManager
final NotificationManager nm = (NotificationManager)
  getSystemService(Context.NOTIFICATION_SERVICE);

// Dispara a notificação
nm.notify(1234, notificacao); 

// Simulando uma tarefa para atualizar a notificação
new Thread(){
  public void run(){
    for (int i = 0; i <= 100; i+=5) {
      // Simula uma tarefa
      try {
         Thread.sleep(1000);
      } catch (InterruptedException e) {
      }

      // Atualiza as views remotas       
      contentView.setProgressBar(
        R.id.barraDeProgresso, 100, i, false);

      contentView.setTextViewText(
        R.id.textoDeProgresso, i+"% concluído");

      // Reenvia a notificação
      nm.notify(1234, notificacao);
    }
  } 
}.start(); 


A utilização de views remotas (RemoteViews) se dá quando queremos acessar views que pertencem a outro processo. No nosso exemplo estamos acessando views que pertencem a aplicação de Notificação. Para atualizar a view, utilizamos uma thread para simular uma tarefa. Entretanto esse trabalho ficaria a cargo de um Service.

O resultado ficará como abaixo:


[Editado em 11/11/2010]
Uma funcionalidade legal da notificação é a possibilidade de tocar um som e acender o led do telefone.
/* 
É possível passar um som personalizado usando:
notificacao.sound = 
   Uri.parse("file:///sdcard/meu.mp3");

Abaixo usamos o som padrão de notificação do telefone
*/
notificacao.defaults = Notification.DEFAULT_SOUND;

// Ativa a flag para habilitar o led do telefone
notificacao.flags |= Notification.FLAG_SHOW_LIGHTS;

// Define o RGB da cor do led 
// (o hardware deixará aproximado)
notificacao.ledARGB = Color.YELLOW;

// Tempo ligado em milisegundos
notificacao.ledOnMS = 1000;

// Tempo desligado em milisegundos
notificacao.ledOffMS = 1000;

Editado em 31/10/2012
O construtor da classe Notification está deprecated. Devemos usar a classe NotificationCompat.Builder para criar as notificações em versões anteriores a 3.0.
PendingIntent pit =
  PendingIntent.getActivity(
    context, 0, new Intent(), 0);

Notification notificacao = 
  new NotificationCompat.Builder(context)
    .setTicker("Chegou uma mensagem do GCM")
    .setContentTitle("Nova mensagem")
    .setContentIntent(pit)
    .setContentText(extras.getString("mensagem"))
    .setSmallIcon(R.drawable.ic_launcher)
    .setAutoCancel(true)
    .build();

NotificationManager manager = (NotificationManager)
  getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notificacao);

Qualquer dúvida, deixem seus comentários,

4br4ç05,
nglauber

4 comentários:

easycode disse...

Olha Glauber, só pra acrescentar. Da pra colocar um número no icone do notification. Feito tem nas notificações das contas de email.

Notification n = new notification();
n.number = 2; // Esse number é o valor desejado

Unknown disse...

O metodo setLatestEventInfo está deprecated.
Nova forma de criar uma notificação
---

Notification.Builder builder = new Notification.Builder(context);
builder.setContentIntent(pi)
.setSmallIcon(R.drawable.icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon))
.setWhen(System.currentTimeMillis()).setAutoCancel(true)
.setContentTitle("Mensagem Superior")
.setContentText("Detalhes");
Notification notification = builder.build();

NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(1234, notification);

Unknown disse...

Glauber e nesse método:

setLatestEventInfo

Qual seria o substituto?

nt.setLatestEventInfo(this, "Status Replicação", "A replicação foi feita com sucesso. Total: " + totalReplicado + " de " + totalDB, p);

Nelson Glauber de Vasconcelos Leal disse...

Oi Unknow,

Na verdade são OS substitutos:

.setTicker("Chegou uma mensagem do GCM") .setContentTitle("Nova mensagem")
.setContentIntent(pit)

4br4ç05,
nglauber