Skip to content

Commit f5b25b5

Browse files
Fix channel and channel group implementation (#750)
#535 started implementing custom channel and channels group creation but there were some missing parts that this PR finishes: - Using the incoming channel in `PostNotification` after creating it - Add channel group name and add this group to the custom channel - Show it in the example app This PR also fixes very old tests Closes #723 Co-authored-by: Yogev Ben David <[email protected]>
1 parent e214fd9 commit f5b25b5

File tree

9 files changed

+91
-71
lines changed

9 files changed

+91
-71
lines changed
Binary file not shown.

example/index.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ class NotificationsExampleApp extends Component {
4646
completion();
4747
});
4848

49-
Notifications.ios.events().appNotificationSettingsLinked(() => {
50-
console.warn('App Notification Settings Linked')
51-
});
49+
if (Platform.OS === 'ios') {
50+
Notifications.ios.events().appNotificationSettingsLinked(() => {
51+
console.warn('App Notification Settings Linked')
52+
});
53+
}
5254
}
5355

5456
requestPermissionsIos(options) {
@@ -93,13 +95,30 @@ class NotificationsExampleApp extends Component {
9395
sound: 'chime.aiff',
9496
category: 'SOME_CATEGORY',
9597
link: 'localNotificationLink',
98+
android_channel_id: 'my-channel',
9699
});
97100
}
98101

99102
removeAllDeliveredNotifications() {
100103
Notifications.removeAllDeliveredNotifications();
101104
}
102105

106+
setNotificationChannel() {
107+
Notifications.setNotificationChannel({
108+
channelId: 'my-channel',
109+
name: 'My Channel',
110+
groupId: 'my-group-id',
111+
groupName: 'my group name',
112+
importance: 5,
113+
description: 'My Description',
114+
enableLights: true,
115+
enableVibration: true,
116+
showBadge: true,
117+
soundFile: 'doorbell.mp3',
118+
vibrationPattern: [200, 1000, 500, 1000, 500],
119+
})
120+
}
121+
103122
async componentDidMount() {
104123
const initialNotification = await Notifications.getInitialNotification();
105124
if (initialNotification) {
@@ -134,7 +153,7 @@ class NotificationsExampleApp extends Component {
134153
{this.renderNotification(notification)}
135154
</View>
136155
));
137-
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
156+
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
138157
(
139158
<View key={`notification_${idx}`}>
140159
{this.renderOpenedNotification(notification)}
@@ -148,6 +167,9 @@ class NotificationsExampleApp extends Component {
148167
<Button title={'Request permissions with provisional'} onPress={() => this.requestPermissionsIos(['Provisional'])} testID={'requestPermissionsWithAppSettings'} />
149168
<Button title={'Request permissions with app notification settings and provisional'} onPress={() => this.requestPermissionsIos(['ProvidesAppNotificationSettings', 'Provisional'])} testID={'requestPermissionsWithAppSettings'} />
150169
</>)}
170+
{Platform.OS === 'android' &&
171+
<Button title={'Set channel'} onPress={this.setNotificationChannel} testID={'setNotificationChannel'} />
172+
}
151173
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'} />
152174
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications} />
153175
{notifications}

lib/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/NotificationChannel.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.wix.reactnativenotifications.core.notification;
22

3+
import android.app.NotificationChannelGroup;
34
import android.app.NotificationManager;
45
import android.content.Context;
56
import android.graphics.Color;
@@ -33,6 +34,9 @@ public void setNotificationChannel() {
3334
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
3435
return;
3536
}
37+
final NotificationManager notificationManager = (NotificationManager) mContext
38+
.getSystemService(Context.NOTIFICATION_SERVICE);
39+
3640
android.app.NotificationChannel channel = new android.app.NotificationChannel(
3741
mNotificationChannelProps.getChannelId(),
3842
mNotificationChannelProps.getName(),
@@ -48,8 +52,13 @@ public void setNotificationChannel() {
4852
if (mNotificationChannelProps.hasEnableVibration()) {
4953
channel.enableVibration(mNotificationChannelProps.getEnableVibration());
5054
}
51-
if (mNotificationChannelProps.hasGroupId()) {
52-
channel.setGroup(mNotificationChannelProps.getGroupId());
55+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mNotificationChannelProps.hasGroupId()) {
56+
final String groupId = mNotificationChannelProps.getGroupId();
57+
if(notificationManager.getNotificationChannelGroup(groupId) == null) {
58+
notificationManager.createNotificationChannelGroup(
59+
new NotificationChannelGroup(groupId, mNotificationChannelProps.getGroupName()));
60+
}
61+
channel.setGroup(groupId);
5362
}
5463
if (mNotificationChannelProps.hasLightColor()) {
5564
channel.setLightColor(Color.parseColor(mNotificationChannelProps.getLightColor()));
@@ -65,9 +74,6 @@ public void setNotificationChannel() {
6574
createVibrationPatternFromList(mNotificationChannelProps.getVibrationPattern())
6675
);
6776
}
68-
69-
final NotificationManager notificationManager = (NotificationManager) mContext
70-
.getSystemService(Context.NOTIFICATION_SERVICE);
7177
notificationManager.createNotificationChannel(channel);
7278
}
7379

lib/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/NotificationChannelProps.java

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ public boolean hasGroupId() {
5252
return mBundle.containsKey("groupId");
5353
}
5454

55+
public String getGroupName() {
56+
String name = mBundle.getString("groupName");
57+
if (name == null) {
58+
name = getGroupId();
59+
}
60+
return name;
61+
}
62+
63+
public boolean hasGroupName() {
64+
return mBundle.containsKey("groupName");
65+
}
66+
5567
public int getImportance() {
5668
return (int) mBundle.getDouble("importance");
5769
}

lib/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java

+16-9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public void onAppVisible() {
4141
public void onAppNotVisible() {
4242
}
4343
};
44+
final private String DEFAULT_CHANNEL_ID = "channel_01";
45+
final private String DEFAULT_CHANNEL_NAME = "Channel Name";
4446

4547
public static IPushNotification get(Context context, Bundle bundle) {
4648
Context appContext = context.getApplicationContext();
@@ -56,6 +58,7 @@ protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade ap
5658
mAppLaunchHelper = appLaunchHelper;
5759
mJsIOHelper = JsIOHelper;
5860
mNotificationProps = createProps(bundle);
61+
initDefaultChannel(context);
5962
}
6063

6164
@Override
@@ -144,10 +147,6 @@ protected Notification buildNotification(PendingIntent intent) {
144147
}
145148

146149
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
147-
148-
String CHANNEL_ID = "channel_01";
149-
String CHANNEL_NAME = "Channel Name";
150-
151150
final Notification.Builder notification = new Notification.Builder(mContext)
152151
.setContentTitle(mNotificationProps.getTitle())
153152
.setContentText(mNotificationProps.getBody())
@@ -158,12 +157,10 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
158157
setUpIcon(notification);
159158

160159
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
161-
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
162-
CHANNEL_NAME,
163-
NotificationManager.IMPORTANCE_DEFAULT);
164160
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
165-
notificationManager.createNotificationChannel(channel);
166-
notification.setChannelId(CHANNEL_ID);
161+
String channelId = mNotificationProps.getChannelId();
162+
NotificationChannel channel = notificationManager.getNotificationChannel(channelId);
163+
notification.setChannelId(channel != null ? channelId : DEFAULT_CHANNEL_ID);
167164
}
168165

169166
return notification;
@@ -226,4 +223,14 @@ protected void launchOrResumeApp() {
226223
private int getAppResourceId(String resName, String resType) {
227224
return mContext.getResources().getIdentifier(resName, resType, mContext.getPackageName());
228225
}
226+
227+
private void initDefaultChannel(Context context) {
228+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
229+
NotificationChannel defaultChannel = new NotificationChannel(DEFAULT_CHANNEL_ID,
230+
DEFAULT_CHANNEL_NAME,
231+
NotificationManager.IMPORTANCE_DEFAULT);
232+
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
233+
notificationManager.createNotificationChannel(defaultChannel);
234+
}
235+
}
229236
}

lib/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationProps.java

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ public String getBody() {
1818
return getBundleStringFirstNotNull("gcm.notification.body", "body");
1919
}
2020

21+
public String getChannelId() {
22+
return getBundleStringFirstNotNull("gcm.notification.android_channel_id", "android_channel_id");
23+
}
24+
2125
public Bundle asBundle() {
2226
return (Bundle) mBundle.clone();
2327
}

lib/android/app/src/test/java/com/wix/reactnativenotifications/core/notification/PushNotificationTest.java

+7-14
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.robolectric.Shadows;
3333
import org.robolectric.shadows.ShadowNotification;
3434

35+
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME;
3536
import static org.junit.Assert.assertEquals;
3637
import static org.junit.Assert.assertNotEquals;
3738
import static org.mockito.ArgumentMatchers.any;
@@ -49,6 +50,7 @@ public class PushNotificationTest {
4950

5051
private static final String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
5152
private static final String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
53+
private static final String NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME = "notificationReceivedBackground";
5254

5355
private static final String DEFAULT_NOTIFICATION_TITLE = "Notification-title";
5456
private static final String DEFAULT_NOTIFICATION_BODY = "Notification-body";
@@ -206,7 +208,7 @@ public void onOpened_reactInitializedWithNoActivities_setAsInitialNotification()
206208
}
207209

208210
@Test
209-
public void onReceived_validData_postNotificationAndNotifyJS() throws Exception {
211+
public void onReceived_validData_dontPostNotificationAndNotifyJS() throws Exception {
210212
// Arrange
211213

212214
setUpForegroundApp();
@@ -219,19 +221,15 @@ public void onReceived_validData_postNotificationAndNotifyJS() throws Exception
219221
// Assert
220222

221223
ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
222-
223-
// Notifications should not be visible while app is in foreground
224224
verify(mNotificationManager, never()).notify(anyInt(), notificationCaptor.capture());
225-
226-
// Notifications should be reported to javascript while app is in background
227225
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
228226
}
229227

230228
@Test
231229
public void onReceived_validDataForBackgroundApp_postNotificationAndNotifyJs() throws Exception {
232230
// Arrange
233231

234-
setUpForegroundApp();
232+
setUpBackgroundApp();
235233

236234
// Act
237235

@@ -241,12 +239,8 @@ public void onReceived_validDataForBackgroundApp_postNotificationAndNotifyJs() t
241239
// Assert
242240

243241
ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
244-
245-
// Notifications should not be visible while app is in foreground
246-
verify(mNotificationManager, never()).notify(anyInt(), notificationCaptor.capture());
247-
248-
// Notifications should be reported to javascript while app is in background
249-
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
242+
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
243+
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
250244
}
251245

252246
@Test
@@ -257,8 +251,7 @@ public void onReceived_validDataForDeadApp_postNotificationDontNotifyJS() throws
257251
ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
258252
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
259253
verifyNotification(notificationCaptor.getValue());
260-
261-
verify(mJsIOHelper, never()).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), any(Bundle.class), any(ReactContext.class));
254+
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(null));
262255
}
263256

264257
@Test
+13-38
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,14 @@
1-
export class NotificationChannel {
2-
channelId: string
3-
name: string
4-
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5
5-
description?: string
6-
enableLights?: boolean
7-
enableVibration?: boolean
8-
groupId?: string
9-
lightColor?: string
10-
showBadge?: boolean
11-
soundFile?: string // "sound_file.mp3" for the file "android/app/src/main/res/raw/sound_file.mp3"
12-
vibrationPattern?: number[]
13-
14-
constructor(
15-
channelId: string,
16-
name: string,
17-
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5,
18-
description?: string,
19-
enableLights?: boolean,
20-
enableVibration?: boolean,
21-
groupId?: string,
22-
lightColor?: string,
23-
showBadge?: boolean,
24-
soundFile?: string,
25-
vibrationPattern?: number[],
26-
) {
27-
this.channelId = channelId;
28-
this.name = name;
29-
this.importance = importance;
30-
this.description = description;
31-
this.enableLights = enableLights;
32-
this.enableVibration = enableVibration;
33-
this.groupId = groupId;
34-
this.lightColor = lightColor;
35-
this.showBadge = showBadge;
36-
this.soundFile = soundFile;
37-
this.vibrationPattern = vibrationPattern;
38-
}
1+
export interface NotificationChannel {
2+
channelId: string;
3+
name: string;
4+
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5;
5+
description?: string;
6+
enableLights?: boolean;
7+
enableVibration?: boolean;
8+
groupId?: string;
9+
groupName?: string;
10+
lightColor?: string;
11+
showBadge?: boolean;
12+
soundFile?: string;
13+
vibrationPattern?: number[];
3914
}

website/docs/api/android-api.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ Notifications.setNotificationChannel({
3030
description: 'My Description',
3131
enableLights: true,
3232
enableVibration: true,
33-
groupId: 'your-group', // optional
33+
groupId: 'my-group', // optional
34+
groupName: 'My Group', // optional, will be presented in Android OS notification permission
3435
showBadge: true,
3536
soundFile: 'custom_sound.mp3', // place this in <project_root>/android/app/src/main/res/raw/custom_sound.mp3
3637
vibrationPattern: [200, 1000, 500, 1000, 500],

0 commit comments

Comments
 (0)