Skip to content

Channels #535

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

Merged
merged 6 commits into from
Apr 8, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import com.wix.reactnativenotifications.core.InitialNotificationHolder;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
import com.wix.reactnativenotifications.core.ReactAppLifecycleFacade;
import com.wix.reactnativenotifications.core.notification.INotificationChannel;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativenotifications.core.notification.NotificationChannel;
import com.wix.reactnativenotifications.core.notification.PushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer;
Expand Down Expand Up @@ -127,6 +129,16 @@ public void isRegisteredForRemoteNotifications(Promise promise) {
notificationsDrawer.onAllNotificationsClearRequest();
}

@ReactMethod
void setNotificationChannel(ReadableMap notificationChannelPropsMap) {
final Bundle notificationChannelProps = Arguments.toBundle(notificationChannelPropsMap);
INotificationChannel notificationsDrawer = NotificationChannel.get(
getReactApplicationContext().getApplicationContext(),
notificationChannelProps
);
notificationsDrawer.setNotificationChannel();
}

protected void startFcmIntentService(String extraFlag) {
final Context appContext = getReactApplicationContext().getApplicationContext();
final Intent tokenFetchIntent = new Intent(appContext, FcmInstanceIdRefreshHandlerService.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.wix.reactnativenotifications.core.notification;

public interface INotificationChannel {

/**
* Creates a new notification channel with the given parameters. This also updates an existing
* notification channel.
*
*/
void setNotificationChannel();

NotificationChannelProps asProps();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.wix.reactnativenotifications.core.notification;

import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;

import java.util.List;

public class NotificationChannel implements INotificationChannel {

final protected Context mContext;
final protected NotificationChannelProps mNotificationChannelProps;

protected NotificationChannel(Context context, Bundle bundle) {
mContext = context;
mNotificationChannelProps = createProps(bundle);
}

public static INotificationChannel get(Context context, Bundle bundle) {
return new NotificationChannel(context, bundle);
}

protected NotificationChannelProps createProps(Bundle bundle) {
return new NotificationChannelProps(bundle);
}

@Override
public void setNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
android.app.NotificationChannel channel = new android.app.NotificationChannel(
mNotificationChannelProps.getChannelId(),
mNotificationChannelProps.getName(),
mNotificationChannelProps.getImportance()
);

if (mNotificationChannelProps.hasDescription()) {
channel.setDescription(mNotificationChannelProps.getDescription());
}
if (mNotificationChannelProps.hasEnableLights()) {
channel.enableLights(mNotificationChannelProps.getEnableLights());
}
if (mNotificationChannelProps.hasEnableVibration()) {
channel.enableVibration(mNotificationChannelProps.getEnableVibration());
}
if (mNotificationChannelProps.hasGroupId()) {
channel.setGroup(mNotificationChannelProps.getGroupId());
}
if (mNotificationChannelProps.hasLightColor()) {
channel.setLightColor(Color.parseColor(mNotificationChannelProps.getLightColor()));
}
if (mNotificationChannelProps.hasShowBadge()) {
channel.setShowBadge(mNotificationChannelProps.getShowBadge());
}
if (mNotificationChannelProps.hasSoundFile()) {
channel.setSound(getSound(mNotificationChannelProps.getSoundFile()), null);
}
if (mNotificationChannelProps.hasVibrationPattern()) {
channel.setVibrationPattern(
createVibrationPatternFromList(mNotificationChannelProps.getVibrationPattern())
);
}

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

@Override
public NotificationChannelProps asProps() {
return mNotificationChannelProps.copy();
}

private long[] createVibrationPatternFromList(List patternRequest) {
if (patternRequest == null) {
return null;
}

long[] pattern = new long[patternRequest.size()];
for (int i = 0; i < patternRequest.size(); i++) {
if (patternRequest.get(i) instanceof Number) {
pattern[i] = ((Number) patternRequest.get(i)).longValue();
}
}
return pattern;
}

private Uri getSound(String sound) {
if (sound == null) {
return null;
} else if (sound.contains("://")) {
return Uri.parse(sound);
} else if (sound.equalsIgnoreCase("default")) {
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
} else {
int soundResourceId = getResourceId("raw", sound);
if (soundResourceId == 0) {
soundResourceId = getResourceId("raw", sound.substring(0, sound.lastIndexOf('.')));
}
return Uri.parse("android.resource://" + mContext.getPackageName() + "/" + soundResourceId);
}
}

private int getResourceId(String type, String image) {
return mContext
.getResources()
.getIdentifier(image, type, mContext.getPackageName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.wix.reactnativenotifications.core.notification;

import android.os.Bundle;

import java.util.List;

public class NotificationChannelProps {

protected Bundle mBundle;

public NotificationChannelProps(Bundle bundle) {
mBundle = bundle;
}

public String getChannelId() {
return mBundle.getString("channelId");
}

public boolean hasChannelId() {
return mBundle.containsKey("channelId");
}

public String getDescription() {
return mBundle.getString("description");
}

public boolean hasDescription() {
return mBundle.containsKey("description");
}

public boolean getEnableLights() {
return mBundle.getBoolean("enableLights");
}

public boolean hasEnableLights() {
return mBundle.containsKey("enableLights");
}

public boolean getEnableVibration() {
return mBundle.getBoolean("enableVibration");
}

public boolean hasEnableVibration() {
return mBundle.containsKey("enableVibration");
}

public String getGroupId() {
return mBundle.getString("groupId");
}

public boolean hasGroupId() {
return mBundle.containsKey("groupId");
}

public int getImportance() {
return (int) mBundle.getDouble("importance");
}

public boolean hasImportance() {
return mBundle.containsKey("importance");
}

public String getLightColor() {
return mBundle.getString("lightColor");
}

public boolean hasLightColor() {
return mBundle.containsKey("lightColor");
}

public String getName() {
return mBundle.getString("name");
}

public boolean hasName() {
return mBundle.containsKey("name");
}

public boolean getShowBadge() {
return mBundle.getBoolean("showBadge");
}

public boolean hasShowBadge() {
return mBundle.containsKey("showBadge");
}

public String getSoundFile() {
return mBundle.getString("soundFile");
}

public boolean hasSoundFile() {
return mBundle.containsKey("soundFile");
}

public List getVibrationPattern() {
return mBundle.getParcelableArrayList("vibrationPattern");
}

public boolean hasVibrationPattern() {
return mBundle.containsKey("vibrationPattern");
}

public Bundle asBundle() {
return (Bundle) mBundle.clone();
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder(1024);
for (String key : mBundle.keySet()) {
sb.append(key).append("=").append(mBundle.get(key)).append(", ");
}
return sb.toString();
}

protected NotificationChannelProps copy() {
return new NotificationChannelProps((Bundle) mBundle.clone());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ protected PushNotification(Context context, Bundle bundle, AppLifecycleFacade ap

@Override
public void onReceived() throws InvalidNotificationException {
postNotification(null);
if (!mAppLifecycleFacade.isAppVisible()) {
postNotification(null);
}
notifyReceivedToJS();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,11 @@ public void onReceived_validData_postNotificationAndNotifyJS() throws Exception
// Assert

ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
verifyNotification(notificationCaptor.getValue());

// 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));
}

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

ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class);
verify(mNotificationManager).notify(anyInt(), notificationCaptor.capture());
verifyNotification(notificationCaptor.getValue());

// 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));
}

Expand Down
8 changes: 8 additions & 0 deletions lib/src/Notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Notification } from './DTO/Notification';
import { UniqueIdProvider } from './adapters/UniqueIdProvider';
import { CompletionCallbackWrapper } from './adapters/CompletionCallbackWrapper';
import { NotificationCategory } from './interfaces/NotificationCategory';
import { NotificationChannel } from './interfaces/NotificationChannel';
import { NotificationsIOS } from './NotificationsIOS';
import { NotificationsAndroid } from './NotificationsAndroid';
import { NotificationFactory } from './DTO/NotificationFactory';
Expand Down Expand Up @@ -92,6 +93,13 @@ export class NotificationsRoot {
return this.commands.isRegisteredForRemoteNotifications();
}

/**
* setNotificationChannel
*/
public setNotificationChannel(notificationChannel: NotificationChannel) {
return this.android.setNotificationChannel(notificationChannel);
}

/**
* Obtain the events registry instance
*/
Expand Down
8 changes: 8 additions & 0 deletions lib/src/NotificationsAndroid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Commands } from './commands/Commands';
import { Platform } from 'react-native';
import { NotificationChannel } from './interfaces/NotificationChannel';

export class NotificationsAndroid {
constructor(private readonly commands: Commands) {
Expand All @@ -20,4 +21,11 @@ export class NotificationsAndroid {
public registerRemoteNotifications() {
this.commands.refreshToken();
}

/**
* setNotificationChannel
*/
public setNotificationChannel(notificationChannel: NotificationChannel) {
return this.commands.setNotificationChannel(notificationChannel);
}
}
6 changes: 6 additions & 0 deletions lib/src/adapters/NativeCommandsSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Notification } from '../DTO/Notification';
import { NotificationCompletion } from '../interfaces/NotificationCompletion';
import { NotificationPermissions } from '../interfaces/NotificationPermissions';
import { NotificationCategory } from '../interfaces/NotificationCategory';
import { NotificationChannel } from '../interfaces/NotificationChannel';

interface NativeCommandsModule {
getInitialNotification(): Promise<Object>;
Expand All @@ -23,6 +24,7 @@ interface NativeCommandsModule {
setCategories(categories: [NotificationCategory?]): void;
finishPresentingNotification(notificationId: string, callback: NotificationCompletion): void;
finishHandlingAction(notificationId: string): void;
setNotificationChannel(notificationChannel: NotificationChannel): void;
}

export class NativeCommandsSender {
Expand Down Expand Up @@ -102,4 +104,8 @@ export class NativeCommandsSender {
finishHandlingAction(notificationId: string): void {
this.nativeCommandsModule.finishHandlingAction(notificationId);
}

setNotificationChannel(notificationChannel: NotificationChannel) {
this.nativeCommandsModule.setNotificationChannel(notificationChannel);
}
}
5 changes: 5 additions & 0 deletions lib/src/commands/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as _ from 'lodash';
import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
import { Notification } from '../DTO/Notification';
import { NotificationCategory } from '../interfaces/NotificationCategory';
import { NotificationChannel } from '../interfaces/NotificationChannel';
import { NotificationPermissions } from '../interfaces/NotificationPermissions';
import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
import { NotificationFactory } from '../DTO/NotificationFactory';
Expand Down Expand Up @@ -86,4 +87,8 @@ export class Commands {
public refreshToken() {
this.nativeCommandsSender.refreshToken();
}

public setNotificationChannel(notificationChannel: NotificationChannel) {
this.nativeCommandsSender.setNotificationChannel(notificationChannel);
}
}
Loading