Skip to content

Commit 085f4b6

Browse files
authored
Merge pull request #56 from urbanairship/clear-notifications
Add notification management support
2 parents 1142f7e + ad40464 commit 085f4b6

15 files changed

+288
-38
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
Version 1.3.0 - November 15, 2017
2+
=================================
3+
- Added APIs to manage active notifications.
4+
15
Version 1.2.3 - October 30, 2017
26
================================
37
- Changed Android Message Center title to be "Message Center" instead of the app name

android/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
22

33
android {
44
compileSdkVersion 26
5-
buildToolsVersion "26.0.0"
5+
buildToolsVersion '26.0.2'
66

77
defaultConfig {
88
minSdkVersion 16

android/src/main/java/com/urbanairship/reactnative/ReactAirshipReceiver.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,18 @@ protected void onChannelUpdated(@NonNull Context context, @NonNull String channe
3737
UrbanAirshipReactModule.checkOptIn(context);
3838
}
3939

40+
4041
@Override
4142
protected void onPushReceived(@NonNull Context context, @NonNull PushMessage message, boolean notificationPosted) {
42-
Event event = new PushReceivedEvent(message);
43+
if (!notificationPosted) {
44+
Event event = new PushReceivedEvent(message);
45+
EventEmitter.shared().sendEvent(context, event);
46+
}
47+
}
48+
49+
@Override
50+
protected void onNotificationPosted(@NonNull Context context, @NonNull NotificationInfo notificationInfo) {
51+
Event event = new PushReceivedEvent(notificationInfo);
4352
EventEmitter.shared().sendEvent(context, event);
4453
}
4554

android/src/main/java/com/urbanairship/reactnative/ReactAutopilot.java

+22
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import android.preference.PreferenceManager;
66
import android.support.annotation.NonNull;
7+
import android.support.v4.app.NotificationCompat;
78

89
import com.urbanairship.Autopilot;
910
import com.urbanairship.UAirship;
@@ -14,9 +15,12 @@
1415
import com.urbanairship.actions.DeepLinkAction;
1516
import com.urbanairship.actions.OpenRichPushInboxAction;
1617
import com.urbanairship.actions.OverlayRichPushMessageAction;
18+
import com.urbanairship.push.PushManager;
1719
import com.urbanairship.reactnative.events.DeepLinkEvent;
1820
import com.urbanairship.reactnative.events.InboxUpdatedEvent;
1921
import com.urbanairship.richpush.RichPushInbox;
22+
import com.urbanairship.push.notifications.DefaultNotificationFactory;
23+
import com.urbanairship.push.PushMessage;
2024

2125
import static com.urbanairship.reactnative.UrbanAirshipReactModule.AUTO_LAUNCH_MESSAGE_CENTER;
2226

@@ -78,6 +82,24 @@ public boolean apply(ActionArguments actionArguments) {
7882
return true;
7983
}
8084
});
85+
86+
87+
DefaultNotificationFactory notificationFactory = new DefaultNotificationFactory(UAirship.getApplicationContext()) {
88+
@Override
89+
public NotificationCompat.Builder extendBuilder(@NonNull NotificationCompat.Builder builder, @NonNull PushMessage message, int notificationId) {
90+
builder.getExtras().putBundle("push_message", message.getPushBundle());
91+
return builder;
92+
}
93+
};
94+
95+
if (airship.getAirshipConfigOptions().notificationIcon != 0) {
96+
notificationFactory.setSmallIconId(airship.getAirshipConfigOptions().notificationIcon);
97+
}
98+
99+
notificationFactory.setColor(airship.getAirshipConfigOptions().notificationAccentColor);
100+
notificationFactory.setNotificationChannel(airship.getAirshipConfigOptions().notificationChannel);
101+
102+
airship.getPushManager().setNotificationFactory(notificationFactory);
81103
}
82104

83105
}

android/src/main/java/com/urbanairship/reactnative/UrbanAirshipReactModule.java

+80-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package com.urbanairship.reactnative;
44

55
import android.Manifest;
6+
import android.app.NotificationManager;
67
import android.content.Context;
78
import android.content.Intent;
89
import android.content.SharedPreferences;
@@ -11,9 +12,12 @@
1112
import android.os.Build;
1213
import android.os.Bundle;
1314
import android.preference.PreferenceManager;
15+
import android.service.notification.StatusBarNotification;
1416
import android.support.annotation.NonNull;
17+
import android.support.v4.app.NotificationManagerCompat;
1518
import android.support.v4.content.ContextCompat;
1619
import android.support.v4.os.AsyncTaskCompat;
20+
import android.util.Log;
1721

1822
import com.facebook.react.bridge.Arguments;
1923
import com.facebook.react.bridge.Dynamic;
@@ -34,10 +38,13 @@
3438
import com.urbanairship.actions.ActionRunRequest;
3539
import com.urbanairship.actions.LandingPageActivity;
3640
import com.urbanairship.analytics.AssociatedIdentifiers;
41+
import com.urbanairship.push.PushMessage;
3742
import com.urbanairship.push.TagGroupsEditor;
3843
import com.urbanairship.reactnative.events.NotificationOptInEvent;
44+
import com.urbanairship.reactnative.events.PushReceivedEvent;
3945
import com.urbanairship.richpush.RichPushInbox;
4046
import com.urbanairship.richpush.RichPushMessage;
47+
import com.urbanairship.util.UAStringUtil;
4148

4249
import java.util.Calendar;
4350
import java.util.Date;
@@ -598,12 +605,84 @@ public void markInboxMessageRead(String messageId, Promise promise) {
598605
}
599606
}
600607

608+
@ReactMethod
609+
public void clearNotifications() {
610+
NotificationManagerCompat.from(UAirship.getApplicationContext()).cancelAll();
611+
}
612+
613+
@ReactMethod
614+
public void clearNotification(String identifier) {
615+
if (UAStringUtil.isEmpty(identifier)) {
616+
return;
617+
}
618+
619+
String[] parts = identifier.split(":", 2);
620+
if (parts.length == 0) {
621+
Log.e(getName(), "Invalid identifier: " + identifier);
622+
return;
623+
}
624+
625+
int id;
626+
String tag = null;
627+
try {
628+
id = Integer.valueOf(parts[0]);
629+
} catch (NumberFormatException e) {
630+
Log.e(getName(), "Invalid identifier: " + identifier);
631+
return;
632+
}
633+
634+
if (parts.length == 2) {
635+
tag = parts[1];
636+
}
637+
638+
639+
NotificationManagerCompat.from(UAirship.getApplicationContext()).cancel(tag, id);
640+
}
641+
642+
643+
601644
/**
602-
* Forces the inbox to refresh. This is normally not needed as the inbox will automatically refresh on foreground or when a push arrives thats associated with a message.
645+
* Retrieves the current inbox messages.
603646
*
604647
* @param promise The JS promise.
605648
*/
606649
@ReactMethod
650+
public void getActiveNotifications(Promise promise) {
651+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
652+
WritableArray notifications = Arguments.createArray();
653+
654+
NotificationManager notificationManager = (NotificationManager) UAirship.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
655+
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
656+
657+
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
658+
int id = statusBarNotification.getId();
659+
String tag = statusBarNotification.getTag();
660+
661+
PushMessage pushMessage;
662+
Bundle extras = statusBarNotification.getNotification().extras;
663+
if (extras != null && extras.containsKey("push_message")) {
664+
pushMessage = new PushMessage(extras.getBundle("push_message"));
665+
} else {
666+
pushMessage = new PushMessage(new Bundle());
667+
}
668+
669+
notifications.pushMap(new PushReceivedEvent(pushMessage, id, tag).getBody());
670+
}
671+
672+
promise.resolve(notifications);
673+
} else {
674+
promise.reject("UNSUPPORTED", "Getting active notifications is only supported on Marshmallow and newer devices.");
675+
}
676+
}
677+
678+
679+
680+
/**
681+
* Forces the inbox to refresh. This is normally not needed as the inbox will automatically refresh on foreground or when a push arrives thats associated with a message.
682+
*
683+
* @param promise The JS promise.
684+
*/
685+
@ReactMethod
607686
public void refreshInbox(final Promise promise) {
608687
UAirship.shared().getInbox().fetchMessages(new RichPushInbox.FetchMessagesCallback() {
609688
@Override

android/src/main/java/com/urbanairship/reactnative/events/NotificationResponseEvent.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public class NotificationResponseEvent implements Event {
2222
private static final String RESPONSE_FOREGROUND = "isForeground";
2323
private static final String RESPONSE_NOTIFICATION = "notification";
2424

25-
2625
private final AirshipReceiver.NotificationInfo notificationInfo;
2726
private final AirshipReceiver.ActionButtonInfo actionButtonInfo;
2827

@@ -56,8 +55,7 @@ public String getName() {
5655
@Override
5756
public WritableMap getBody() {
5857
WritableMap map = Arguments.createMap();
59-
map.putMap(RESPONSE_NOTIFICATION, new PushReceivedEvent(notificationInfo.getMessage()).getBody());
60-
58+
map.putMap(RESPONSE_NOTIFICATION, new PushReceivedEvent(notificationInfo).getBody());
6159

6260
if (actionButtonInfo != null) {
6361
map.putString(RESPONSE_ACTION_ID, actionButtonInfo.getButtonId());
@@ -66,6 +64,7 @@ public WritableMap getBody() {
6664
map.putBoolean(RESPONSE_FOREGROUND, true);
6765
}
6866

67+
6968
return map;
7069
}
7170

android/src/main/java/com/urbanairship/reactnative/events/PushReceivedEvent.java

+45
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
package com.urbanairship.reactnative.events;
44

5+
import android.app.Notification;
56
import android.os.Bundle;
67
import android.support.annotation.NonNull;
78

89
import com.facebook.react.bridge.Arguments;
910
import com.facebook.react.bridge.WritableMap;
1011
import com.urbanairship.push.PushMessage;
12+
import com.urbanairship.AirshipReceiver;
1113
import com.urbanairship.reactnative.Event;
14+
import com.urbanairship.util.UAStringUtil;
15+
1216

1317

1418
/**
@@ -20,8 +24,11 @@ public class PushReceivedEvent implements Event {
2024
private static final String PUSH_ALERT = "alert";
2125
private static final String PUSH_TITLE = "title";
2226
private static final String PUSH_EXTRAS = "extras";
27+
private static final String NOTIFICATION_ID = "notificationId";
2328

2429
private final PushMessage message;
30+
private Integer notificationId;
31+
private String notificationTag;
2532

2633
/**
2734
* Default constructor.
@@ -32,6 +39,30 @@ public PushReceivedEvent(@NonNull PushMessage message) {
3239
this.message = message;
3340
}
3441

42+
/**
43+
* Default constructor.
44+
*
45+
* @param notificationInfo The posted notification info.
46+
*/
47+
public PushReceivedEvent(@NonNull AirshipReceiver.NotificationInfo notificationInfo) {
48+
this.message = notificationInfo.getMessage();
49+
this.notificationId = notificationInfo.getNotificationId();
50+
this.notificationTag = notificationInfo.getNotificationTag();
51+
}
52+
53+
/**
54+
* Default constructor.
55+
*
56+
* @param message The push message.
57+
*/
58+
public PushReceivedEvent(@NonNull PushMessage message, int notificationId, String notificationTag) {
59+
this.message = message;
60+
this.notificationId = notificationId;
61+
this.notificationTag = notificationTag;
62+
}
63+
64+
65+
3566
@NonNull
3667
@Override
3768
public String getName() {
@@ -51,6 +82,10 @@ public WritableMap getBody() {
5182
map.putString(PUSH_TITLE, message.getTitle());
5283
}
5384

85+
if (notificationId != null) {
86+
map.putString(NOTIFICATION_ID, getNotificationdId(notificationId, notificationTag));
87+
}
88+
5489
Bundle bundle = new Bundle(message.getPushBundle());
5590
bundle.remove("android.support.content.wakelockid");
5691
map.putMap(PUSH_EXTRAS, Arguments.fromBundle(bundle));
@@ -62,4 +97,14 @@ public WritableMap getBody() {
6297
public boolean isCritical() {
6398
return false;
6499
}
100+
101+
102+
static String getNotificationdId(int notificationId, String notificationTag) {
103+
String id = String.valueOf(notificationId);
104+
if (!UAStringUtil.isEmpty(notificationTag)) {
105+
id += ":" + notificationTag;
106+
}
107+
108+
return id;
109+
}
65110
}

ios/UARCTModule/UARCTEventEmitter.h

+8
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,12 @@ extern NSString *const UARCTNotificationPresentationSoundKey;
4848
*/
4949
- (void)inboxUpdated;
5050

51+
/**
52+
* Creates a push map for a given notification content.
53+
* @param content The notification content.
54+
* @return Push map.
55+
*/
56+
+ (NSMutableDictionary *)eventBodyForNotificationContent:(UANotificationContent *)content;
57+
58+
5159
@end

ios/UARCTModule/UARCTEventEmitter.m

+9-4
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ -(void)deepLinkReceived:(NSString *)deepLink {
8989
-(void)receivedForegroundNotification:(UANotificationContent *)notificationContent
9090
completionHandler:(void (^)(void))completionHandler {
9191

92-
[self sendEventWithName:UARCTPushReceivedEventName body:[self eventBodyForNotificationContent:notificationContent]];
92+
[self sendEventWithName:UARCTPushReceivedEventName body:[UARCTEventEmitter eventBodyForNotificationContent:notificationContent]];
9393
completionHandler();
9494
}
9595

9696
-(void)receivedBackgroundNotification:(UANotificationContent *)notificationContent
9797
completionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
9898

99-
[self sendEventWithName:UARCTPushReceivedEventName body:[self eventBodyForNotificationContent:notificationContent]];
99+
[self sendEventWithName:UARCTPushReceivedEventName body:[UARCTEventEmitter eventBodyForNotificationContent:notificationContent]];
100100
completionHandler(UIBackgroundFetchResultNoData);
101101
}
102102

@@ -176,7 +176,7 @@ - (void)inboxUpdated {
176176

177177
- (NSMutableDictionary *)eventBodyForNotificationResponse:(UANotificationResponse *)notificationResponse {
178178
NSMutableDictionary *body = [NSMutableDictionary dictionary];
179-
[body setValue:[self eventBodyForNotificationContent:notificationResponse.notificationContent] forKey:@"notification"];
179+
[body setValue:[UARCTEventEmitter eventBodyForNotificationContent:notificationResponse.notificationContent] forKey:@"notification"];
180180

181181
if ([notificationResponse.actionIdentifier isEqualToString:UANotificationDefaultActionIdentifier]) {
182182
[body setValue:@(YES) forKey:@"isForeground"];
@@ -192,7 +192,7 @@ - (NSMutableDictionary *)eventBodyForNotificationResponse:(UANotificationRespons
192192
return body;
193193
}
194194

195-
- (NSMutableDictionary *)eventBodyForNotificationContent:(UANotificationContent *)content {
195+
+ (NSMutableDictionary *)eventBodyForNotificationContent:(UANotificationContent *)content {
196196
NSMutableDictionary *pushBody = [NSMutableDictionary dictionary];
197197
[pushBody setValue:content.alertBody forKey:@"alert"];
198198
[pushBody setValue:content.alertTitle forKey:@"title"];
@@ -212,6 +212,11 @@ - (NSMutableDictionary *)eventBodyForNotificationContent:(UANotificationContent
212212
[pushBody setValue:extras forKey:@"extras"];
213213
}
214214

215+
if (@available(iOS 10.0, *)) {
216+
NSString *identifier = content.notification.request.identifier;
217+
[pushBody setValue:identifier forKey:@"notificationId"];
218+
}
219+
215220
return pushBody;
216221
}
217222

0 commit comments

Comments
 (0)