Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix channel and channel group implementation #750

Merged
merged 5 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
30 changes: 26 additions & 4 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ class NotificationsExampleApp extends Component {
completion();
});

Notifications.ios.events().appNotificationSettingsLinked(() => {
console.warn('App Notification Settings Linked')
});
if (Platform.OS === 'ios') {
Notifications.ios.events().appNotificationSettingsLinked(() => {
console.warn('App Notification Settings Linked')
});
}
}

requestPermissionsIos(options) {
Expand Down Expand Up @@ -93,13 +95,30 @@ class NotificationsExampleApp extends Component {
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
link: 'localNotificationLink',
android_channel_id: 'my-channel',
});
}

removeAllDeliveredNotifications() {
Notifications.removeAllDeliveredNotifications();
}

setNotificationChannel() {
Notifications.setNotificationChannel({
channelId: 'my-channel',
name: 'My Channel',
groupId: 'my-group-id',
groupName: 'my group name',
importance: 5,
description: 'My Description',
enableLights: true,
enableVibration: true,
showBadge: true,
soundFile: 'doorbell.mp3',
vibrationPattern: [200, 1000, 500, 1000, 500],
})
}

async componentDidMount() {
const initialNotification = await Notifications.getInitialNotification();
if (initialNotification) {
Expand Down Expand Up @@ -134,7 +153,7 @@ class NotificationsExampleApp extends Component {
{this.renderNotification(notification)}
</View>
));
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderOpenedNotification(notification)}
Expand All @@ -148,6 +167,9 @@ class NotificationsExampleApp extends Component {
<Button title={'Request permissions with provisional'} onPress={() => this.requestPermissionsIos(['Provisional'])} testID={'requestPermissionsWithAppSettings'} />
<Button title={'Request permissions with app notification settings and provisional'} onPress={() => this.requestPermissionsIos(['ProvidesAppNotificationSettings', 'Provisional'])} testID={'requestPermissionsWithAppSettings'} />
</>)}
{Platform.OS === 'android' &&
<Button title={'Set channel'} onPress={this.setNotificationChannel} testID={'setNotificationChannel'} />
}
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'} />
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications} />
{notifications}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wix.reactnativenotifications.core.notification;

import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
Expand Down Expand Up @@ -33,6 +34,9 @@ public void setNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
final NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);

android.app.NotificationChannel channel = new android.app.NotificationChannel(
mNotificationChannelProps.getChannelId(),
mNotificationChannelProps.getName(),
Expand All @@ -48,8 +52,13 @@ public void setNotificationChannel() {
if (mNotificationChannelProps.hasEnableVibration()) {
channel.enableVibration(mNotificationChannelProps.getEnableVibration());
}
if (mNotificationChannelProps.hasGroupId()) {
channel.setGroup(mNotificationChannelProps.getGroupId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mNotificationChannelProps.hasGroupId()) {
final String groupId = mNotificationChannelProps.getGroupId();
if(notificationManager.getNotificationChannelGroup(groupId) == null) {
notificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(groupId, mNotificationChannelProps.getGroupName()));
}
channel.setGroup(groupId);
}
if (mNotificationChannelProps.hasLightColor()) {
channel.setLightColor(Color.parseColor(mNotificationChannelProps.getLightColor()));
Expand All @@ -65,9 +74,6 @@ public void setNotificationChannel() {
createVibrationPatternFromList(mNotificationChannelProps.getVibrationPattern())
);
}

final NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ public boolean hasGroupId() {
return mBundle.containsKey("groupId");
}

public String getGroupName() {
String name = mBundle.getString("groupName");
if (name == null) {
name = getGroupId();
}
return name;
}

public boolean hasGroupName() {
return mBundle.containsKey("groupName");
}

public int getImportance() {
return (int) mBundle.getDouble("importance");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public void onAppVisible() {
public void onAppNotVisible() {
}
};
final private String DEFAULT_CHANNEL_ID = "channel_01";
final private String DEFAULT_CHANNEL_NAME = "Channel Name";

public static IPushNotification get(Context context, Bundle bundle) {
Context appContext = context.getApplicationContext();
Expand All @@ -56,6 +58,7 @@ protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade ap
mAppLaunchHelper = appLaunchHelper;
mJsIOHelper = JsIOHelper;
mNotificationProps = createProps(bundle);
initDefaultChannel(context);
}

@Override
Expand Down Expand Up @@ -144,10 +147,6 @@ protected Notification buildNotification(PendingIntent intent) {
}

protected Notification.Builder getNotificationBuilder(PendingIntent intent) {

String CHANNEL_ID = "channel_01";
String CHANNEL_NAME = "Channel Name";

final Notification.Builder notification = new Notification.Builder(mContext)
.setContentTitle(mNotificationProps.getTitle())
.setContentText(mNotificationProps.getBody())
Expand All @@ -158,12 +157,10 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
setUpIcon(notification);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
notification.setChannelId(CHANNEL_ID);
String channelId = mNotificationProps.getChannelId();
NotificationChannel channel = notificationManager.getNotificationChannel(channelId);
notification.setChannelId(channel != null ? channelId : DEFAULT_CHANNEL_ID);
}

return notification;
Expand Down Expand Up @@ -226,4 +223,14 @@ protected void launchOrResumeApp() {
private int getAppResourceId(String resName, String resType) {
return mContext.getResources().getIdentifier(resName, resType, mContext.getPackageName());
}

private void initDefaultChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel defaultChannel = new NotificationChannel(DEFAULT_CHANNEL_ID,
DEFAULT_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(defaultChannel);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public String getBody() {
return getBundleStringFirstNotNull("gcm.notification.body", "body");
}

public String getChannelId() {
return getBundleStringFirstNotNull("gcm.notification.android_channel_id", "android_channel_id");
}

public Bundle asBundle() {
return (Bundle) mBundle.clone();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowNotification;

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

private static final String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
private static final String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
private static final String NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME = "notificationReceivedBackground";

private static final String DEFAULT_NOTIFICATION_TITLE = "Notification-title";
private static final String DEFAULT_NOTIFICATION_BODY = "Notification-body";
Expand Down Expand Up @@ -206,7 +208,7 @@ public void onOpened_reactInitializedWithNoActivities_setAsInitialNotification()
}

@Test
public void onReceived_validData_postNotificationAndNotifyJS() throws Exception {
public void onReceived_validData_dontPostNotificationAndNotifyJS() throws Exception {
// Arrange

setUpForegroundApp();
Expand All @@ -219,19 +221,15 @@ public void onReceived_validData_postNotificationAndNotifyJS() throws Exception
// Assert

ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);

// Notifications should not be visible while app is in foreground
verify(mNotificationManager, never()).notify(anyInt(), notificationCaptor.capture());

// Notifications should be reported to javascript while app is in background
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
}

@Test
public void onReceived_validDataForBackgroundApp_postNotificationAndNotifyJs() throws Exception {
// Arrange

setUpForegroundApp();
setUpBackgroundApp();

// Act

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

ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);

// Notifications should not be visible while app is in foreground
verify(mNotificationManager, never()).notify(anyInt(), notificationCaptor.capture());

// Notifications should be reported to javascript while app is in background
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(mReactContext));
}

@Test
Expand All @@ -257,8 +251,7 @@ public void onReceived_validDataForDeadApp_postNotificationDontNotifyJS() throws
ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
verifyNotification(notificationCaptor.getValue());

verify(mJsIOHelper, never()).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), any(Bundle.class), any(ReactContext.class));
verify(mJsIOHelper).sendEventToJS(eq(NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME), argThat(new isValidNotification(mNotificationBundle)), eq(null));
}

@Test
Expand Down
51 changes: 13 additions & 38 deletions lib/src/interfaces/NotificationChannel.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
export class NotificationChannel {
channelId: string
name: string
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5
description?: string
enableLights?: boolean
enableVibration?: boolean
groupId?: string
lightColor?: string
showBadge?: boolean
soundFile?: string // "sound_file.mp3" for the file "android/app/src/main/res/raw/sound_file.mp3"
vibrationPattern?: number[]

constructor(
channelId: string,
name: string,
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5,
description?: string,
enableLights?: boolean,
enableVibration?: boolean,
groupId?: string,
lightColor?: string,
showBadge?: boolean,
soundFile?: string,
vibrationPattern?: number[],
) {
this.channelId = channelId;
this.name = name;
this.importance = importance;
this.description = description;
this.enableLights = enableLights;
this.enableVibration = enableVibration;
this.groupId = groupId;
this.lightColor = lightColor;
this.showBadge = showBadge;
this.soundFile = soundFile;
this.vibrationPattern = vibrationPattern;
}
export interface NotificationChannel {
channelId: string;
name: string;
importance: -1000 | 0 | 1 | 2 | 3 | 4 | 5;
description?: string;
enableLights?: boolean;
enableVibration?: boolean;
groupId?: string;
groupName?: string;
lightColor?: string;
showBadge?: boolean;
soundFile?: string;
vibrationPattern?: number[];
}
3 changes: 2 additions & 1 deletion website/docs/api/android-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Notifications.setNotificationChannel({
description: 'My Description',
enableLights: true,
enableVibration: true,
groupId: 'your-group', // optional
groupId: 'my-group', // optional
groupName: 'My Group', // optional, will be presented in Android OS notification permission
showBadge: true,
soundFile: 'custom_sound.mp3', // place this in <project_root>/android/app/src/main/res/raw/custom_sound.mp3
vibrationPattern: [200, 1000, 500, 1000, 500],
Expand Down