Skip to content

Commit

Permalink
feat(local-notifications): Add delivered notification handling (#1060)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcesarmobile authored Jun 28, 2022
1 parent f9575f5 commit 0a89dc9
Show file tree
Hide file tree
Showing 6 changed files with 379 additions and 1 deletion.
73 changes: 73 additions & 0 deletions local-notifications/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ If the device has entered [Doze](https://developer.android.com/training/monitori
* [`registerActionTypes(...)`](#registeractiontypes)
* [`cancel(...)`](#cancel)
* [`areEnabled()`](#areenabled)
* [`getDeliveredNotifications()`](#getdeliverednotifications)
* [`removeDeliveredNotifications(...)`](#removedeliverednotifications)
* [`removeAllDeliveredNotifications()`](#removealldeliverednotifications)
* [`createChannel(...)`](#createchannel)
* [`deleteChannel(...)`](#deletechannel)
* [`listChannels()`](#listchannels)
Expand Down Expand Up @@ -185,6 +188,51 @@ Check if notifications are enabled or not.
--------------------


### getDeliveredNotifications()

```typescript
getDeliveredNotifications() => Promise<DeliveredNotifications>
```

Get a list of notifications that are visible on the notifications screen.

**Returns:** <code>Promise&lt;<a href="#deliverednotifications">DeliveredNotifications</a>&gt;</code>

**Since:** 4.0.0

--------------------


### removeDeliveredNotifications(...)

```typescript
removeDeliveredNotifications(delivered: DeliveredNotifications) => Promise<void>
```

Remove the specified notifications from the notifications screen.

| Param | Type |
| --------------- | ------------------------------------------------------------------------- |
| **`delivered`** | <code><a href="#deliverednotifications">DeliveredNotifications</a></code> |

**Since:** 4.0.0

--------------------


### removeAllDeliveredNotifications()

```typescript
removeAllDeliveredNotifications() => Promise<void>
```

Remove all the notifications from the notifications screen.

**Since:** 4.0.0

--------------------


### createChannel(...)

```typescript
Expand Down Expand Up @@ -547,6 +595,31 @@ An action that can be taken when a notification is displayed.
| **`value`** | <code>boolean</code> | Whether or not the device has local notifications enabled. | 1.0.0 |


#### DeliveredNotifications

| Prop | Type | Description | Since |
| ------------------- | ------------------------------------------ | ------------------------------------------------------------------- | ----- |
| **`notifications`** | <code>DeliveredNotificationSchema[]</code> | List of notifications that are visible on the notifications screen. | 1.0.0 |


#### DeliveredNotificationSchema

| Prop | Type | Description | Since |
| ------------------ | --------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----- |
| **`id`** | <code>number</code> | The notification identifier. | 4.0.0 |
| **`tag`** | <code>string</code> | The notification tag. Only available on Android. | 4.0.0 |
| **`title`** | <code>string</code> | The title of the notification. | 4.0.0 |
| **`body`** | <code>string</code> | The body of the notification, shown below the title. | 4.0.0 |
| **`group`** | <code>string</code> | The configured group of the notification. Only available for Android. | 4.0.0 |
| **`groupSummary`** | <code>boolean</code> | If this notification is the summary for a group of notifications. Only available for Android. | 4.0.0 |
| **`data`** | <code>any</code> | Any additional data that was included in the notification payload. Only available for Android. | 4.0.0 |
| **`extra`** | <code>any</code> | Extra data to store within this notification. Only available for iOS. | 4.0.0 |
| **`attachments`** | <code>Attachment[]</code> | The attachments for this notification. Only available for iOS. | 1.0.0 |
| **`actionTypeId`** | <code>string</code> | <a href="#action">Action</a> type ssociated with this notification. Only available for iOS. | 4.0.0 |
| **`schedule`** | <code><a href="#schedule">Schedule</a></code> | <a href="#schedule">Schedule</a> used to fire this notification. Only available for iOS. | 4.0.0 |
| **`sound`** | <code>string</code> | Sound that was used when the notification was displayed. Only available for iOS. | 4.0.0 |


#### Channel

| Prop | Type | Description | Default | Since |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.capacitorjs.plugins.localnotifications;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import com.getcapacitor.Bridge;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
Expand All @@ -13,12 +18,15 @@
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

@CapacitorPlugin(name = "LocalNotifications", permissions = @Permission(strings = {}, alias = "display"))
public class LocalNotificationsPlugin extends Plugin {

private static Bridge staticBridge = null;
private LocalNotificationManager manager;
public NotificationManager notificationManager;
private NotificationStorage notificationStorage;
private NotificationChannelManager notificationChannelManager;

Expand All @@ -29,6 +37,7 @@ public void load() {
manager = new LocalNotificationManager(notificationStorage, getActivity(), getContext(), this.bridge.getConfig());
manager.createNotificationChannel();
notificationChannelManager = new NotificationChannelManager(getActivity());
notificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
staticBridge = this.bridge;
}

Expand Down Expand Up @@ -97,6 +106,76 @@ public void areEnabled(PluginCall call) {
call.resolve(data);
}

@PluginMethod
public void getDeliveredNotifications(PluginCall call) {
JSArray notifications = new JSArray();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();

for (StatusBarNotification notif : activeNotifications) {
JSObject jsNotif = new JSObject();

jsNotif.put("id", notif.getId());
jsNotif.put("tag", notif.getTag());

Notification notification = notif.getNotification();
if (notification != null) {
jsNotif.put("title", notification.extras.getCharSequence(Notification.EXTRA_TITLE));
jsNotif.put("body", notification.extras.getCharSequence(Notification.EXTRA_TEXT));
jsNotif.put("group", notification.getGroup());
jsNotif.put("groupSummary", 0 != (notification.flags & Notification.FLAG_GROUP_SUMMARY));

JSObject extras = new JSObject();

for (String key : notification.extras.keySet()) {
extras.put(key, notification.extras.get(key));
}

jsNotif.put("data", extras);
}

notifications.put(jsNotif);
}
}

JSObject result = new JSObject();
result.put("notifications", notifications);
call.resolve(result);
}

@PluginMethod
public void removeDeliveredNotifications(PluginCall call) {
JSArray notifications = call.getArray("notifications");

try {
for (Object o : notifications.toList()) {
if (o instanceof JSONObject) {
JSObject notif = JSObject.fromJSONObject((JSONObject) o);
String tag = notif.getString("tag");
Integer id = notif.getInteger("id");

if (tag == null) {
notificationManager.cancel(id);
} else {
notificationManager.cancel(tag, id);
}
} else {
call.reject("Expected notifications to be a list of notification objects");
}
}
} catch (JSONException e) {
call.reject(e.getMessage());
}

call.resolve();
}

@PluginMethod
public void removeAllDeliveredNotifications(PluginCall call) {
notificationManager.cancelAll();
call.resolve();
}

@PluginMethod
public void createChannel(PluginCall call) {
notificationChannelManager.createChannel(call);
Expand Down
3 changes: 3 additions & 0 deletions local-notifications/ios/Plugin/LocalNotificationsPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
CAP_PLUGIN_METHOD(getPending, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(registerActionTypes, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(areEnabled, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(getDeliveredNotifications, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(removeAllDeliveredNotifications, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(removeDeliveredNotifications, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(createChannel, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(deleteChannel, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(listChannels, CAPPluginReturnPromise);
Expand Down
39 changes: 39 additions & 0 deletions local-notifications/ios/Plugin/LocalNotificationsPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,45 @@ public class LocalNotificationsPlugin: CAPPlugin {
return opts
}

/**
* Get notifications in Notification Center
*/
@objc func getDeliveredNotifications(_ call: CAPPluginCall) {
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: { (notifications) in
let ret = notifications.map({ (notification) -> [String: Any] in
return self.notificationDelegationHandler.makeNotificationRequestJSObject(notification.request)
})
call.resolve([
"notifications": ret
])
})
}

/**
* Remove specified notifications from Notification Center
*/
@objc func removeDeliveredNotifications(_ call: CAPPluginCall) {
guard let notifications = call.getArray("notifications", JSObject.self) else {
call.reject("Must supply notifications to remove")
return
}

let ids = notifications.map { "\($0["id"] ?? "")" }
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ids)
call.resolve()
}

/**
* Remove all notifications from Notification Center
*/
@objc func removeAllDeliveredNotifications(_ call: CAPPluginCall) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
DispatchQueue.main.async(execute: {
UIApplication.shared.applicationIconBadgeNumber = 0
})
call.resolve()
}

@objc func createChannel(_ call: CAPPluginCall) {
call.unimplemented()
}
Expand Down
Loading

0 comments on commit 0a89dc9

Please sign in to comment.