I am using the react-native-firebase library and I am brainstorming to customize my notifications when they are in the background, I tried to follow the documentation but to no avail, are there native code .java to handle notifications?
To customize the background notifications you can add in the index of your application:
import { AppRegistry } from 'react-native'
import App from './App'
import { backgroundMessageNotificationHandler } from 'src/common/notifications'
AppRegistry.registerComponent('testApp', () => App)
AppRegistry.registerHeadlessTask(
'RNFirebaseBackgroundMessage',
() => backgroundMessageNotificationHandler)
The backgroundMessageNotificationHandler could be something like that:
export const backgroundMessageNotificationHandler = async (message: any) => {
const localNotification = new firebase.notifications.Notification().android
.setChannelId(androidNotificationsChannel.channelId)
.android.setSmallIcon('iconBlackAndWhite')
.android.setLargeIcon('iconBlackAndWhite')
.android.setPriority(firebase.notifications.Android.Priority.High)
.setNotificationId(message.messageId)
.setSound('default')
.setTitle(message.data.title)
.setBody(message.data.body)
.setData(message.data)
if (Platform.OS === 'android') {
createChannel()
}
firebase.notifications().displayNotification(localNotification)
return Promise.resolve()
}
Remember to follow the steps to setting up the build.gralde, MainActivity and AndroidManifest. You can follow the steps specified in the following post:
React-Native Android Push Notification via Firebase
Related
TLDR: Ibeacon module example does not work
I have a small app in Ionic 5 using capacitor.
I want to use the Ibeacon library, but I get the error :
Ressource for the library is scarse and I have only found people having issue when the delegate is undefined causing the LocatonManager error here.
I also tried to look what is causing the error, apparently the device mentioned is part of the device library. So I check if the Ibeacon library properly import the device one and it does in node_modules\cordova-plugin-ibeacon\plugin.xml, like so :
<!-- Version is set to anything because the only feature we use is the device.platform property which was available
since forever. The added benefit is that we don't force the consumers of this plugin to use a certain version of
the device plugin. -->
<dependency id="cordova-plugin-device" version="*" />
My class is pretty much the example given in the Ibeacon page:
import { Component, OnInit } from '#angular/core';
import { IBeacon } from '#ionic-native/ibeacon/ngx';
import { Platform } from '#ionic/angular';
#Component({
selector: 'app-beacon',
templateUrl: './beacon.page.html',
styleUrls: ['./beacon.page.scss'],
})
export class BeaconPage implements OnInit {
public beacons: any[] = [];
constructor(
private ibeacon: IBeacon,
private platform: Platform,
private _utils: UtilsService
) {}
ngOnInit() {
console.log('ngOnInit');
if (!this.platform.is('android')) {
console.log('Beacon related activity only available on Android');
return;
}
// create a new delegate and register it with the native layer
let delegate = this.ibeacon.Delegate();
console.log('delegate :', delegate);
// Subscribe to some of the delegate's event handlers
delegate.didRangeBeaconsInRegion().subscribe(
(data) => console.log('didRangeBeaconsInRegion: ', data),
(error) => console.error()
);
delegate.didStartMonitoringForRegion().subscribe(
(data) => console.log('didStartMonitoringForRegion: ', data),
(error) => console.error()
);
delegate.didEnterRegion().subscribe((data) => {
console.log('didEnterRegion: ', data);
});
let beaconRegion = this.ibeacon.BeaconRegion(
'deskBeacon',
'F7826DA6-ASDF-ASDF-8024-BC5B71E0893E'
);
this.ibeacon.startMonitoringForRegion(beaconRegion).then(
() => console.log('Native layer received the request to monitoring'),
(error) =>
console.error('Native layer failed to begin monitoring: ', error)
);
}
}
Also I imported the IBeacon module inside my module.ts like so :
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { IonicModule } from '#ionic/angular';
import { BeaconPageRoutingModule } from './beacon-routing.module';
import { BeaconPage } from './beacon.page';
import { IBeacon } from '#ionic-native/ibeacon/ngx';
#NgModule({
imports: [CommonModule, FormsModule, IonicModule, BeaconPageRoutingModule],
declarations: [BeaconPage],
providers: [IBeacon],
})
export class BeaconPageModule {}
Did I forget to do something ? Why is device undefined ? Should I also import the device library ?
I should mention I have the device library installed.
Inside the lib they use the device to check the plataform, that is the code:
BeaconRegion.isValidUuid = function (uuid) {
// https://github.com/petermetz/cordova-plugin-ibeacon/issues/328
// If we are on Android, then allow the UUID to be specified as a wild-card (omitted)
var isAndroid = device && device.platform === "Android";
if (uuid === BeaconRegion.WILDCARD_UUID && isAndroid) {
return true;
}
var uuidValidatorRegex = this.getUuidValidatorRegex();
return uuid.match(uuidValidatorRegex) != null;
};
You can check right here https://github.com/petermetz/cordova-plugin-ibeacon/blob/270ffbbc12159861a16e5e81481103c1e09139cb/www/model/BeaconRegion.js#L38
So, you have to install the following plugin-in https://ionicframework.com/docs/native/device
npm install cordova-plugin-device
npm install #ionic-native/device
ionic cap sync
Then the find this device reference and the problem will be solved.
I've tried everything to get push notifications for Android to work with Pinpoint in my react native app.
The iOS integration works as expected.
In android, i'm able to record the endpoint, but when I export the endpoint from pinpoint, I can see that the field that controls push notification--OptOut--is set to ALL.
This is the integration that I have in my App.js to record the endpoints:
import React, {Component} from 'react';
import { StyleSheet, View, PushNotificationIOS, AsyncStorage, Text } from 'react-native'
//aws
import Amplify, { Auth } from 'aws-amplify'
import aws_exports from './aws-exports'
import PushNotification from '#aws-amplify/pushnotification'
Analytics.configure(aws_exports);
Amplify.configure(aws_exports)
console.log('exports',aws_exports)
PushNotification.configure(aws_exports);
type Props = {};
class App extends Component<Props> {
componentDidMount(){
console.log(PushNotification)
// get the notification data when notification is received
PushNotification.onNotification((notification) => {
// Note that the notification object structure is different from Android and IOS
console.log('in app notification', notification);
// required on iOS only (see fetchCompletionHandler docs: https://facebook.github.io/react-native/docs/pushnotificationios.html)
notification.finish(PushNotificationIOS.FetchResult.NoData);
});
// get the registration token
// This will only be triggered when the token is generated or updated.
PushNotification.onRegister((token) => {
console.log('in app registration', token);
});
}
render() {
...
}
}
export default codePush(codePushOptions)(App)
I am implementing a react-native app that receives firebase push notification. When a notification arrives the app navigate to a screen to show the notification.
I followed this approach using reference:
"Navigating without the navigation prop"
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
When I tested this with debug mode, it works perfectly. But when I tested in release mode (android signed app) it does not work.
Especially, when the notification arrives when the app is open, it does not work. There is no error message and the app become freezing and in a 30 seconds or so, the app crashes.
Here are package info:
"react": "16.8.3",
"react-i18next": "10.12.2",
"react-native": "0.59.10",
"react-native-firebase": "5.5.6",
"react-native-gesture-handler": "1.3.0",
"react-navigation": "3.11.1",
Basically, I tried this
"Navigating without the navigation prop"
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
Similarly, this one too:
https://github.com/react-navigation/react-navigation/issues/742
I am using not class component but functional component.
// Navigator.js
const switchNavigator = createSwitchNavigator({
ResolveAuth: ResolveAuthScreen,
loginFlow: createStackNavigator({
Signin: SigninScreen,
Signup: SignupScreen
}),
helpFlow: createStackNavigator({
Help: HelpScreen,
}, {headerLayoutPreset: 'center'}),
mainFlow: createBottomTabNavigator({
Ask: createStackNavigator({
AskMain: AskScreen,
AskWait: AskWaitScreen,
}, {headerLayoutPreset: 'center'}),
Chat: createStackNavigator({
ChatList: ChatListScreen,
Chatting: ChatScreen,
}, {headerLayoutPreset: 'center'}),
Profile: createStackNavigator({
Account: AccountScreen,
AccountEdit: AccountEditScreen,
ProfileContract: ProfileScreen
}
, {headerLayoutPreset: 'center'})
},
...
export default createAppContainer(switchNavigator);
// App.js
import Navigator from './Navigator';
import { useTranslation } from 'react-i18next';
import { navigate, setNavigator } from './src/navigationRef';
const App = Navigator;
export default () => {
// setup language
const { t } = useTranslation();
// use effect
useEffect(() => {
// notification listener (triggered when a particular notification has been received)
// if the app is foreground, we need to navigate the screen
const listenerFG = firebase.notifications().onNotification((notification: Notification) => {
console.log('onNotification', notification);
Alert.alert(
t('AppScreen.title'),
t('AppScreen.message'),
[
{text: t('yes'), onPress: () => navigate('Help', { notificationBody: notification })},
],
{cancelable: true},
);
});
listenerForAppClosed();
return () => {
listenerFG();
}
}, []);
return (
<App ref={(navigator) => { setNavigator(navigator) }} />
);
// navigationRef.js
import { NavigationActions } from 'react-navigation';
let navigator;
// nav is coming from react navigation
export const setNavigator = navRef => {
console.log('navigation ref', navRef);
// set navigator
navigator = navRef;
};
export const navigate = (routeName, params) => {
console.log('[navigate dispatch] navigator', navigator);
navigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
};
In debug mode, using `navigate('any screen') works like a charm, but in release mode, it does not work.
But one strange thing is that the following navigation works. A user opens a push notification when the app is not foreground state.
// part of App.js
// listen the notification being opened or clicked when the app is closed
const listenerForAppClosed = async() => {
// app closed
const notificationOpen: NotificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
// app was opened by a notification
console.log('getInitialNotification', notificationOpen);
// get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
//// ignore the same notification id since the same notification is received again, don't know why.
// get noti id from storage
const notiId = await AsyncStorage.getItem('notiId');
// set noti id to storage
await AsyncStorage.setItem('notiId', notification.notificationId);
if (notification.notificationId === notiId) {
console.log('notification id is the same');
} else {
console.log('navigating to helpscreen...');
// navigate to Help screen
navigate('Help', { notificationBody: notification });
}
}
}
The problem happens both on Android emulator and a device (Android9).
Why the navigate('Help') does not work in release mode? I searched many documents and I feel that it should work in release mode too.
Is there any other way to navigate to a screen from top-level (like App.js)?
I found the source of the problem.
I tested several things.
I wanted to know that whether very simple app in release mode navigates properly.
So, I just followed this posting:
https://medium.com/#katharinep/firebase-notification-integration-in-react-native-0-60-3a8d6c8d56ff
Here are what I did:
- created two screens: Home and Notification.
- Re-created the app with latest react-native#0.60.6 and react-navigation#4.0.9
- sent cloud message not from the app but from the firebase cloud messaging
It worked! When a notificaiton arrives the app navigated to the notification screen.
So I tried to track down the source of the problem.
- tried to add more screens
- added more providers and context
- sent message from the app
Finally, I found the source. It was how I used 'navigateRef.js'
Originally I used it like this:
// App.js
import { navigate, setNavigator } from './src/navigationRef';
<App ref={(navigator) => { setNavigator(navigator) }} />
// navigationRef.js
import { NavigationActions } from 'react-navigation';
let navigator;
// nav is coming from react navigation
export const setNavigator = navRef => {
console.log('navigation ref', navRef);
// set navigator
navigator = navRef;
};
export const navigate = (routeName, params) => {
console.log('[navigate dispatch] navigator', navigator);
navigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
};
I simply used the exact the code from react-navigation:
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
// App.js
import NavigationService from './src/NavigationService';
<App
ref={navigationRef =>
{NavigationService.setTopLevelNavigator(navigationRef);}}
/>
// NavigationService.js
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
}
Then I worked! I do not know the difference of these two codes.
The first one worked perfectly in debug mode but not in release mode, especially the app is in foreground.
Could anyone tell me the difference? Why the first code does not work?
I am currently using the headless task to receive a push notification and show the local notification. I am currently using react-native-firebase to integrate firebase cloud messaging.
export default bgMessaging = (message) => {
console.log('hello', message)
return Promise.resolve();
}
This is the piece of code which I am using to create task.
And in index.js
AppRegistry.registerHeadlessTask('RNFirebaseBackgroundMessage', () => bgMessaging);
I am using this to registerHeadlessTask.
But when the app is in the killed state I am not getting any console message and don't if my task is running.
Need help to know how can I debug the background task and show custom notification for the killed state.
I solved it as follows:
// index.js
import { AppRegistry, Alert } from 'react-native';
import messaging from '#react-native-firebase/messaging';
import App from './App';
import { name as appName } from './app.json';
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
//receive the message in the background
messaging().onMessage(async teste => {
console.log('teste:', teste);
const novo = (title, message) => {
return Alert.alert(title, message);
};
novo(teste.notification.title, teste.notification.body);
});
AppRegistry.registerComponent(appName, () => App);
This is because we have to validate when the push message arrives in the background in the app
User is able to turn off app notification on Android. Is there a way to check if the user did so?
The React Native doc did not mention Notification permission as it's not a permission that requires prompting the user.
Android docs did mention the ability to access notification policy.
I have used this one in previous experiences with good results:
https://github.com/jigaryadav/react-native-check-notification-enable
Basically what I ended up implementing as helpers were this two functions:
export const getNotificationStatus = async () => {
if (Platform.OS === 'ios') {
const status = await Permissions.check(PermissionRequested.NOTIFICATION)
if (status === PermissionsStatus.AUTHORIZED) {
return true
} else {
return false
}
} else {
// This is where I am using the library
const status = await NotificationManager.areNotificationsEnabled()
return status
}
}
export const openNotificationsSettings = () => {
if (Platform.OS === 'ios') {
Linking.openURL('app-settings:')
} else {
AndroidOpenSettings.appNotificationSettings()
}
}
Just to explain a bit the whole snippet:
I am also using these libraries in conjunction to achieve all: Check if they are enabled and navigate to the settings page to revoke if needed
import AndroidOpenSettings from 'react-native-android-open-settings'
import NotificationManager from 'react-native-check-notification-enable'
import Permissions from 'react-native-permissions'
I have found one library called React-native-permission-settings
It is available for android only so if you need iOS support also i think you need to modify it.
You can follow installation guide from here
And implement it in your code
import NotificationSettings from 'react-native-permission-settings';
...
NotificationSettings.areNotificationsEnabled((isEnabled: boolean) => {
console.log(`Notifications are enabled: ${isEnabled}`);
});