Como descartar a notificação após o clique da ação

142

Desde o nível 16 da API (Jelly Bean), existe a possibilidade de adicionar ações a uma notificação com

builder.addAction(iconId, title, intent);

Mas quando adiciono uma ação a uma notificação e a ação é pressionada, a notificação não será descartada. Quando a notificação em si está sendo clicada, ela pode ser descartada com

notification.flags = Notification.FLAG_AUTO_CANCEL;

ou

builder.setAutoCancel(true);

Mas, obviamente, isso não tem nada a ver com as ações associadas à notificação.

Alguma dica? Ou isso ainda não faz parte da API? Não encontrei nada.

endowzoner
fonte

Respostas:

154

Ao ligar para notificar no gerenciador de notificações, você forneceu um ID - esse é o ID exclusivo que você pode usar para acessá-lo mais tarde (este é do gerenciador de notificações:

notify(int id, Notification notification)

Para cancelar, você deve ligar para:

cancel(int id)

com o mesmo id. Então, basicamente, você precisa acompanhar o id ou possivelmente colocá-lo em um pacote que você adicionar ao Intent dentro do PendingIntent?

Kaediil
fonte
25
Obrigado, isso resolveu meu problema. No entanto, ainda acho que é um pouco complicado demais. Em vez de apenas fornecer uma API para dispensar automaticamente a notificação quando uma ação é pressionada, você deve trabalhar com a intenção e a identificação da notificação passando para obter a mesma.
endowzoner
2
Se você acha que isso é complicado, não tente atualizar uma notificação (não perca o ID) ou verifique se ela é exibida ou não (a API não rastreia, você precisa) ...: P
Travis
2
@Daksh: Basicamente, você adiciona a tag e o ID de notificação à sua intenção, que começa quando sua ação é pressionada. Com essas informações extras, é possível verificar na atividade inicial se foi iniciada por meio de uma ação de notificação.
Endowzoner 19/12/12
5
Exemplo de código de onCreate (): Bundle extras = getIntent (). GetExtras (); if (extras! = null) {String tag = extras.getString (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_TAG); int id = extras.getInt (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_ID); if (NotificationReceiver.NOTIFICATION_ID == id && NotificationReceiver.NOTIFICATION_TAG.equals (tag)) {// a atividade foi iniciada via ação de notificação, dispensa // notificação NotificationManager manager = (NotificationManager) getSystemService (Service.NOTIFICATION_SERVICE); manager.cancel (tag, ID); }}
endowzoner
1
Na nova API você tem notificar (tag String, int id, notificação Notificação) e correspondentemente cancelar (tag String, int id)
Malachiasz
64

Constatou que este é um problema ao usar a notificação Heads Up Display do Lollipop. Consulte as diretrizes de design . Aqui está o código (ish) completo a ser implementado.

Até agora, ter um botão 'Dispensar' era menos importante, mas agora está mais na sua cara.

notificação heads-up

Construindo a notificação

int notificationId = new Random().nextInt(); // just use a counter in some util class...
PendingIntent dismissIntent = NotificationActivity.getDismissIntent(notificationId, context);

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(NotificationCompat.PRIORITY_MAX) //HIGH, MAX, FULL_SCREEN and setDefaults(Notification.DEFAULT_ALL) will make it a Heads Up Display Style
        .setDefaults(Notification.DEFAULT_ALL) // also requires VIBRATE permission
        .setSmallIcon(R.drawable.ic_action_refresh) // Required!
        .setContentTitle("Message from test")
        .setContentText("message")
        .setAutoCancel(true)
        .addAction(R.drawable.ic_action_cancel, "Dismiss", dismissIntent)
        .addAction(R.drawable.ic_action_boom, "Action!", someOtherPendingIntent);

// Gets an instance of the NotificationManager service
NotificationManager notifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// Builds the notification and issues it.
notifyMgr.notify(notificationId, builder.build());

NotificationActivity

public class NotificationActivity extends Activity {

    public static final String NOTIFICATION_ID = "NOTIFICATION_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(getIntent().getIntExtra(NOTIFICATION_ID, -1));
        finish(); // since finish() is called in onCreate(), onDestroy() will be called immediately
    }

    public static PendingIntent getDismissIntent(int notificationId, Context context) {
        Intent intent = new Intent(context, NotificationActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.putExtra(NOTIFICATION_ID, notificationId);
        PendingIntent dismissIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        return dismissIntent;
    }

}

AndroidManifest.xml (atributos necessários para impedir que o SystemUI se concentre em uma pilha traseira)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
aaronvargas
fonte
Eu tenho várias notificações de um aplicativo e a notifaaction está definida como notificação em andamento. Eu quero limpar a notificação quando executa Addaction de notificação perticular
Prasad
3
Não seria mais eficaz usar um BroadcastReceiver aqui para descartar a notificação? Aqui está um bom exemplo que mostra a implementação, mas pode ser ainda mais reduzida: stackoverflow.com/a/19745745/793150
alice.harrison
1
As soluções funcionam, exceto que os extras configurados são encaminhados com a intenção. Eles não são encaminhados para onCreate. Uma maneira é usar variáveis ​​estáticas. Alguém sabe por que os extras com intenção não são encaminhados?
Baschi 11/02
Por que getDismissIntent funciona apenas quando colocado no NotificationActivity? A dispensa Intent não funcionará se o código de criação PendingIntent for colocado na classe do construtor de notificações. Passei apenas duas horas nessa questão e não consigo descobrir por que a Intenção Pendente DEVE ser criada na Atividade. Alguém pode explicar por que esse é o caso?
Raio Li
getDismissIntent () é uma função estática "auxiliar" que cria a intenção correta a ser usada para se comunicar com o NotificationActivity. Como tal, eles geralmente são fornecidos com a Atividade. Mas não vejo por que essa função estática não pôde ser colocada na classe do construtor de notificações, desde que você tenha cuidado ao definir o NOTIFICATION_ID e o contexto corretamente.
Mike
17

Descobri que quando você usa os botões de ação nas notificações expandidas, precisa escrever um código extra e fica mais restrito.

Você deve cancelar manualmente sua notificação quando o usuário clicar em um botão de ação. A notificação é cancelada apenas automaticamente para a ação padrão.

Além disso, se você iniciar um receptor de transmissão a partir do botão, a gaveta de notificação não fechará.

Acabei criando uma nova NotificationActivity para resolver esses problemas. Essa atividade intermediária sem interface do usuário cancela a notificação e inicia a atividade que eu realmente queria iniciar a partir da notificação.

Publiquei um código de amostra em uma postagem relacionada. Clicar em Ações de notificação do Android não fecha a gaveta de notificações .

Vicki
fonte
2
Pena que eles ainda não inseriram isso na API ... É muito hackeado fazer isso dessa maneira. Mas ainda é a única maneira, especialmente se você não tem controle sobre a intenção de destino, como visualizar um URL.
Bogdan Zurac
Obrigado pela informação, você está correto. No entanto gostaria de usar um intentservice ao invés de uma actividade de intermediário
Tim
7

Você sempre pode fazer cancel()o que Notificationquer que esteja sendo chamado pela ação (por exemplo, na onCreate()atividade vinculada à qual PendingIntentvocê fornece addAction()).

CommonsWare
fonte
2
Mas como obtenho acesso à notificação na atividade que foi chamada?
Endowzoner
@FleshWound: cancel()pega o ID do Notification, que você usou quando ligou notify(). Você não precisa do Notificationobjeto.
CommonsWare
O cancelamento (id) do @CommonsWare parou de funcionar se o setGroup estiver definido e houver uma notificação de resumo do Grupo. Nesse caso, o cancelamento não faz nada por algum motivo. Sem o resumo grupo, cancelar funciona bem embora
Kushan
se minha intenção pendente é ACTION_VIEWe o tipo é image/jpeg(para compartilhar uma imagem com outro aplicativo), como esse cancelamento deve ser acionado? O IMO Android deve cancelar automaticamente, estou confuso sobre o motivo pelo qual o Android não cuida apenas dele ?!
Alguém em algum lugar
@ SomeoneSomewhere: "então como esse cancelamento deve ser acionado?" - não pode. Embora não haja nada que o impeça de apontar para um aplicativo de terceiros em um aplicativo Notificationrelacionado PendingIntent, não foi exatamente assim que ele foi projetado para funcionar e, portanto, você terá problemas como este. "O IMO Android deve cancelar automaticamente" - eu pude oferecer uma sinalização para isso na ação, mas não deve ser uma coisa de todos os tempos. Se fosse, pular uma faixa em uma notificação do music player fecharia a notificação.
CommonsWare
7

Na minha opinião, usando um BroadcastReceiver é uma maneira mais limpa de cancelar uma notificação:

No AndroidManifest.xml:

<receiver 
    android:name=.NotificationCancelReceiver" >
    <intent-filter android:priority="999" >
         <action android:name="com.example.cancel" />
    </intent-filter>
</receiver>

No arquivo java:

Intent cancel = new Intent("com.example.cancel");
PendingIntent cancelP = PendingIntent.getBroadcast(context, 0, cancel, PendingIntent.FLAG_CANCEL_CURRENT);

NotificationCompat.Action actions[] = new NotificationCompat.Action[1];

NotificationCancelReceiver

public class NotificationCancelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //Cancel your ongoing Notification
    };
}
Himanshu Khandelwal
fonte
1
É o que faço, mas como você obtém o ID da notificação (neste exemplo, 0) do método onReceive? Não está no Intent, pois não foi adicionado a ele. Eu tentei adicionar-lo como um extra, mas parece que o ID notificação real não é o que eu estava adicionando como um extra na atividade criando ...: - /
Marco Zanetti
Eu realmente gosto dessa abordagem de usar serviços de transmissão em vez de atividades, é uma abordagem muito mais leve.
Christophe Moine
Mas eu tive que usar <intent.setAction (Integer.toString (notificationId));> em adidition para poder descartar qualquer notificação mostrada.
Christophe Moine
1
@MarcoZanetti, você precisa gerar um ID de notificação que você passa para a intenção pendente e também para o método de notificação ao enviar a notificação. Se você fizer isso, quando o usuário clicar na ação para cancelar, chamará o receptor de transmissão e poderá obter o ID da notificação dos extras.
Ray Hunter
@ChristopheMoine você pode colocar id via intent.putExtra()e obtê-lo emBroadcastReceiver
Vadim Kotov
5

Nas novas APIs, não se esqueça do TAG:

notify(String tag, int id, Notification notification)

e correspondentemente

cancel(String tag, int id) 

ao invés de:

cancel(int id)

https://developer.android.com/reference/android/app/NotificationManager

Malachiasz
fonte
Você estava certo! embora a cancel()função tenha 2 implementações; um com TAG e outro sem. Mas temos que fornecer um TAG. Aqui está a cancelfunção dos documentos public void cancel(@Nullable String tag, int id). Última verificação no Android Q
sud007 14/04
1

Basta colocar esta linha:

 builder.setAutoCancel(true);

E o código completo é:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(android.R.drawable.ic_dialog_alert);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.co.in/"));
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    builder.setContentIntent(pendingIntent);
    builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.misti_ic));
    builder.setContentTitle("Notifications Title");
    builder.setContentText("Your notification content here.");
    builder.setSubText("Tap to view the website.");
    Toast.makeText(getApplicationContext(), "The notification has been created!!", Toast.LENGTH_LONG).show();

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    builder.setAutoCancel(true);
    // Will display the notification in the notification bar
    notificationManager.notify(1, builder.build());
Hanisha
fonte
AutoCancel parece ter nenhum efeito quando targetting Android 9 (multa funcionou quando targetting Android 8.1)
Alix
a distinção aqui é com ação
Someone Somewhere
0

Você precisará executar o código a seguir depois que sua intenção for acionada para remover a notificação.

NotificationManagerCompat.from(this).cancel(null, notificationId);

NB: notificationId é o mesmo ID passado para executar sua notificação

Houssin Boulla
fonte
-3

builder.setAutoCancel (true);

Testado no Android 9 também.

Rudakovsky
fonte