Как закрыть уведомление после нажатия на действие

149

Начиная с уровня API 16 (Jelly Bean), есть возможность добавлять действия в уведомление с помощью

builder.addAction(iconId, title, intent);

Но когда я добавляю действие к уведомлению, и действие нажимается, уведомление не будет закрыто. При нажатии на само уведомление его можно закрыть с помощью

notification.flags = Notification.FLAG_AUTO_CANCEL;

или

builder.setAutoCancel(true);

Но, очевидно, это не имеет ничего общего с действиями, связанными с уведомлением.

Есть подсказки? Или это еще не часть API? Ничего не нашел.

эндаузонер
источник

Ответы:

158

Когда вы вызвали notify в диспетчере уведомлений, вы дали ему идентификатор - это уникальный идентификатор, который вы можете использовать для доступа к нему позже (это из диспетчера уведомлений:

notify(int id, Notification notification)

Для отмены звоните:

cancel(int id)

с тем же идентификатором. Итак, в принципе, вам нужно отслеживать идентификатор или, возможно, поместить идентификатор в Bundle, который вы добавляете в Intent внутри PendingIntent?

Kaediil
источник
26
Спасибо, это решило мою проблему. Однако я все еще думаю, что это немного слишком сложно. Вместо того, чтобы просто предоставлять API для автоматического отклонения уведомления при нажатии действия, вы должны работать с передачей намерения и идентификатора уведомления для достижения того же.
endowzoner
2
Если вы думаете, что это сложно, не смотрите на обновление уведомления (не теряйте отслеживание этого идентификатора) или проверку того, отображается ли оно или нет (api не отслеживает его, вы должны) ...: P
Travis
2
@Daksh: По сути, вы добавляете тег уведомления и идентификатор к своему намерению, которое запускается при нажатии вашего действия. С помощью этой дополнительной информации вы можете проверить в стартовом действии, было ли оно запущено с помощью действия уведомления.
endowzoner
5
Пример кода от 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)) {// действие было запущено с помощью действия уведомления, отклонить // уведомление NotificationManager manager = (NotificationManager) getSystemService (Service.NOTIFICATION_SERVICE); manager.cancel (тег, идентификатор); }}
endowzoner
2
В новом API у вас есть уведомление (тег String, int id, уведомление о уведомлении) и, соответственно, отмена (тег String, int id)
Malachiasz
64

Обнаружил, что это проблема при использовании уведомления Heads Up Display от Lollipop. См. Рекомендации по дизайну . Вот полный код для реализации.

Раньше наличие кнопки «Закрыть» было менее важным, но теперь это больше для вас.

уведомление

Создание уведомления

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 (атрибуты, необходимые для предотвращения фокусировки SystemUI на задний стек)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
Ааронваргас
источник
У меня есть несколько уведомлений от одного приложения, и для notifaaction установлено текущее уведомление. Я хочу очистить уведомление, когда добавочное действие выполняется при пертикулярном уведомлении
Прасад
3
Не было бы более эффективным использовать BroadcastReceiver, чтобы вместо этого отклонить уведомление? Вот хороший пример, демонстрирующий реализацию, но ее можно еще уменьшить: stackoverflow.com/a/19745745/793150
alice.harrison
1
Решения работают, за исключением того, что дополнительные функции отправлены с намерением. Они не отправляются в onCreate. Один из способов - использовать статические переменные. Кто-нибудь знает, почему не пересылаются умыслы?
Baschi
Почему getDismissIntent работает только при размещении в NotificationActivity? Intent отклонения не работает, если код создания PendingIntent помещен в класс построителя уведомлений. Я потратил 2 часа на эту проблему и не могу понять, почему ожидающее намерение ДОЛЖНО быть создано в действии. Кто-нибудь может объяснить, почему это так?
Рэй Ли
getDismissIntent () - это статическая «вспомогательная» функция, которая создает правильное намерение для использования для связи с NotificationActivity. Таким образом, они обычно входят в состав Activity. Но я не понимаю, почему эта статическая функция не может быть помещена в класс построителя уведомлений, если вы внимательно установите NOTIFICATION_ID и контекст правильно.
Майк
17

Я обнаружил, что когда вы используете кнопки действий в расширенных уведомлениях, вам нужно писать дополнительный код, и вы более ограничены.

Вам необходимо вручную отменить уведомление, когда пользователь нажимает кнопку действия. Уведомление отменяется автоматически только для действия по умолчанию.

Также, если вы запускаете широковещательный приемник с помощью кнопки, панель уведомлений не закрывается.

В итоге я создал новую NotificationActivity для решения этих проблем. Эта промежуточная активность без какого-либо пользовательского интерфейса отменяет уведомление, а затем запускает действие, которое я действительно хотел начать с уведомления.

Я разместил образец кода в соответствующем сообщении. Щелчок по действиям с уведомлениями Android не закрывает ящик уведомлений .

Вики
источник
2
Жаль, что они до сих пор не встроили это в API ... Довольно хакерски делать это вот так. Но все же единственный способ, особенно если у вас нет никакого контроля над целью назначения, например, просмотр URL-адреса.
Богдан Зурац
Спасибо за информацию, вы правы. Однако я бы использовал службу намерений, а не посредническую деятельность
Тим
7

Вы можете всегда от того, что в настоящее время вызывается действием (например, в деятельности , привязанной к вам поставлять ).cancel()NotificationonCreate()PendingIntentaddAction()

CommonsWare
источник
2
Но как мне получить доступ к уведомлению в вызванном действии?
endowzoner
@FleshWound: cancel()берет идентификатор Notification, который вы использовали при вызове notify(). Вам не нужен Notificationобъект.
CommonsWare,
@CommonsWare cancel (id) перестает работать, если установлен setGroup и есть сводное уведомление о группе. В этом случае отмена по какой-то причине ничего не делает. Без сводки по группе отмена работает нормально
Кушан
если мое ожидающее намерение, ACTION_VIEWа тип - image/jpeg(поделиться изображением с другим приложением), то как должна срабатывать эта отмена? ИМО Android должен автоматически отменять, я озадачен, почему Android просто не заботится об этом ?!
Someone Somewhere
@SomeoneSomewhere: "тогда как должна сработать эта отмена?" - не может. Хотя ничто не мешает вам указать на стороннее приложение в Notificationсвязанном с ним PendingIntent, это не совсем то, как оно было разработано для работы, и поэтому вы столкнетесь с проблемами, подобными этой. «IMO Android должен автоматически отменяться» - я мог видеть предложение флага для этого в действии, но это не должно происходить постоянно. Если бы это было так, пропуск трека в уведомлении музыкального проигрывателя закрывал бы уведомление.
CommonsWare,
7

На мой взгляд, использование a BroadcastReceiver- более чистый способ отменить уведомление:

В AndroidManifest.xml:

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

В 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
    };
}
Химаншу Ханделвал
источник
1
Это то, что я делаю, но как получить идентификатор уведомления (в этом примере 0) из метода onReceive? Этого нет в намерении, поскольку он не был добавлен к нему. Я попытался добавить его в качестве дополнительного, но похоже, что настоящий идентификатор уведомления не тот, который я добавлял в качестве дополнительного в процессе создания ...: - /
Marco Zanetti
Мне очень нравится этот подход к использованию сервисов Broadcast вместо Activity, это гораздо более легкий подход, imho.
Christophe Moine
Но мне пришлось использовать <intent.setAction (Integer.toString (notificationId));> в дополнение, чтобы иметь возможность отклонить любое из показанных уведомлений.
Christophe Moine
1
@MarcoZanetti вам необходимо сгенерировать идентификатор уведомления, который вы передаете ожидающему намерению, а также методу уведомления при отправке уведомления. Если вы это сделаете, то, когда пользователь щелкнет действие для отмены, он вызовет широковещательный приемник, а затем вы сможете получить идентификатор уведомления из дополнительных услуг.
Ray Hunter
@ChristopheMoine, вы можете ввести идентификатор через intent.putExtra()и получить егоBroadcastReceiver
Вадим Котов
6

В новых API не забывайте о TAG:

notify(String tag, int id, Notification notification)

и соответственно

cancel(String tag, int id) 

вместо того:

cancel(int id)

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

Малахиас
источник
Ты был прав! хотя cancel()функция имеет 2 реализации; один с TAG и один без. Но мы должны предоставить TAG. Вот cancelфункция из документов public void cancel(@Nullable String tag, int id). Последний раз проверялось на Android Q
sud007
2

Просто поставьте эту строку:

 builder.setAutoCancel(true);

И полный код:

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());
Ханиша
источник
AutoCancel, похоже, не действует при настройке на Android 9 (отлично работает при настройке на Android 8.1)
Аликс
различие здесь в действиях
Someone Somewhere
2

Вам нужно будет запустить следующий код после того, как будет запущено ваше намерение удалить уведомление.

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

NB: notificationId - это тот же идентификатор, который был передан для запуска вашего уведомления.

Хуссен Булла
источник
-3

builder.setAutoCancel (истина);

Также протестировано на Android 9.

рудаковский
источник