App is working perfectly fine when receiving push notifications on background, but it crashes when in foregrond. I am using RN 0.49 and react-native-fcm 11.3.1 package.
My code looks as below:
FCM.on(FCMEvent.Notification, async (notif) => {
// there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
if(notif.local_notification){
// return;
console.log("1111");
this.showLocalNotification(notif);
}
if(notif.opened_from_tray){
//iOS: app is open/resumed because user clicked banner
//Android: app is open/resumed because user clicked banner or tapped app icon
// return;
console.log("2222");
this.showLocalNotification(notif);
}
// await someAsyncCall();
if(Platform.OS ==='ios'){
//optional
//iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application.
//This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override
//notif._notificationType is available for iOS platfrom
switch(notif._notificationType){
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData) //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All) //other types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, (token) => {
console.log("---------Refresh Token-------------")
console.log(token)
// fcm token may not be available on first load, catch it here
});
showLocalNotification(notif) {
FCM.presentLocalNotification({
title: notif.title,
body: notif.body,
priority: "high",
click_action: notif.click_action,
show_in_foreground: true,
local: true
});
}
componentDidMount(){
// iOS: show permission prompt for the first call. later just check permission in user settings
// Android: check permission in user settings
FCM.requestPermissions().then(()=>console.log('granted')).catch(()=>console.log('notification permission rejected'));
FCM.getFCMToken().then(token => {
console.log(token)
// store fcm token in your server
});
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
console.warn("listening.......................");
});
// initial notification contains the notification that launchs the app. If user launchs app by clicking banner, the banner notification info will be here rather than through FCM.on event
// sometimes Android kills activity when app goes to background, and when resume it broadcasts notification before JS is run. You can use FCM.getInitialNotification() to capture those missed events.
// initial notification will be triggered all the time even when open app by icon so send some action identifier when you send notification
FCM.getInitialNotification().then(notif => {
console.log(notif)
});
}
MainApplication.java
public class MainApplication extends MultiDexApplication {
// Needed for `react-native link`
public List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
// Add your own packages here!
// TODO: add cool native modules
// new MainReactPackage(),
new RNBackgroundGeolocation(),
// Needed for `react-native link`
new FIRMessagingPackage(),
//new RNBackgroundGeolocation(),
//new RNFirebasePackage(),
new VectorIconsPackage()
//new RNFirebaseMessagingPackage()
);
}
}
Android Studio gives this error:
java.lang.ClassCastException: x.y.z.MainApplication
cannot be cast to com.facebook.react.ReactApplication
at
com.evollu.react.fcm.MessagingService$1.run(MessagingService.java:41)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6682)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
I don't know what it is causing the app that it is crashing only on foreground state when the app is open!
Any idea on how to solve this problem?
Related
Using AppCenter I am able to send push notification to all my devices with my Xamarin Forms (android only) app.
Since my devices, are going to be shared, I can't do the filter of the notifications on the AppCenter side based on the devices IDs.
I need to make the filter based on the current logged in user to my application. For this with the push notification I also send the WorkerID, which serves as a filter.
While I'm able to do this filter when the app is in foreground, its not working when the app is in background or not running.(normal behaviour since the push event is in App Start)
protected override void OnStart()
{
// This should come before AppCenter.Start() is called
// Avoid duplicate event registration:
if (!AppCenter.Configured)
{
Push.PushNotificationReceived += (sender, e) =>
{
var title = e.Title;
var message = e.Message;
// If app is in background title and message are null
// App in foreground
if (!string.IsNullOrEmpty(title))
{
foreach (string key in e.CustomData.Keys)
{
if (e.CustomData[key] == Settings.WorkerID)
{
Current.MainPage.DisplayAlert(title, message, "OK");
}
}
}
};
}
// Handle when your app starts
AppCenter.Start("android=xxxxxxxxxxxxxxxxxx", typeof(Push));
}
Is there a way to intercept and filter the push notifications when the app is in background and block them when the app is not running (since no user is yet logged in) ?
To filter users when sending push notifications it's preferable to set user id:
AppCenter.SetUserId("your-user-id");
Then on the appcenter.ms go to Push > Send notificaiton. Fill in the required fields.
And then in the second step select User list instead of All registered devices. Then enter user ids, separated by commas.
Through the document, you can enable-or-disable-push-at-runtime, so can enable or disable push when use go to background or go back to foreground, like this:
protected override void OnSleep()
{
// Handle when your app sleeps
Push.SetEnabledAsync(false);
}
protected override void OnResume()
{
// Handle when your app resumes
Push.SetEnabledAsync(true);
}
You can also enable or disable push when user login or login out:
public void userLogin() {
Push.SetEnabledAsync(true);
}
public void userLoginOut() {
Push.SetEnabledAsync(false);
}
Set it in the right place to meet your requirement.
Im using react-native-firebase for handling push notification for our React Native app (for android and iOS).
I noticed that there is only have 1 callback for a push notification that is received when the app is running (foreground or background) and not when its closed or killed.
firebase
.notifications()
.onNotification(notification => {
console.log('Notification received');
);
But if the app is closed or killed, it will just put the notification in the tray and will not execute the console.log above.
Then enter silent push notification. So when I just send data part in the payload of the notification and even if app is in foreground, the callback above wont be triggered.
I don't see other callbacks that would help on receiving silent push notifications.
So how do we handle push notification in the javascript part?
You don't need additional packages like suggested in other answers.
Use RNFirebase.io, you can handle this easily.
If you receive Notification if App is in Background, you have to handle it by your own to display this Notification. As an example see my init-Method for Push-Notifications.
import firebase from 'react-native-firebase';
const notifications = firebase.notifications();
....
notifications.onNotification((notif) => {
notif.android.setChannelId('app-infos');
notifications.displayNotification(notif);
});
You do it with displayNotification. But make sure, that you set the Notification-Channel before calling it, because else it wouldn't work on >= Android 8.0
BTW: Make sure, that you fully setup Firebase and grant all needed Permissions to be able to listen for Notifications if App is closed or in Background. (https://rnfirebase.io/docs/v5.x.x/notifications/android)
Appendix
I add this as example to show how I implemented the firebase-notification-stuff as a tiny library (remove the redux-stuff if you don't need it):
import firebase from 'react-native-firebase';
import { saveNotificationToken } from 'app/actions/firebase';
import reduxStore from './reduxStore';
import NavigationService from './NavigationService';
const messaging = firebase.messaging();
const notifications = firebase.notifications();
const crashlytics = firebase.crashlytics();
function registerNotifChannels() {
try {
// Notification-Channels is a must-have for Android >= 8
const channel = new firebase.notifications.Android.Channel(
'app-infos',
'App Infos',
firebase.notifications.Android.Importance.Max,
).setDescription('General Information');
notifications.android.createChannel(channel);
} catch (error) {
crashlytics.log(`Error while creating notification-channel \n ${error}`);
}
}
// This is the Promise object that we use to initialise the push
// notifications. It will resolve when the token was successfully retrieved. The
// token is returned as the value of the Promise.
const initPushNotifs = new Promise(async (resolve, reject) => {
try {
const isPermitted = await messaging.hasPermission();
if (isPermitted) {
registerNotifChannels();
try {
const token = await messaging.getToken();
if (token) {
resolve(token);
}
} catch (error) {
crashlytics.log(`Error: failed to get notification-token \n ${error}`);
}
}
} catch (error) {
crashlytics.log(`Error while checking notification-permission\n ${error}`);
}
// If we get this far then there was no token available (or something went
// wrong trying to get it)
reject();
});
function init() {
// Initialise the push notifications, then save the token when/if it's available
initPushNotifs.then(token => reduxStore.dispatch(saveNotificationToken(token)));
// Save the (new) token whenever it changes
messaging.onTokenRefresh(token => reduxStore.dispatch(saveNotificationToken(token)));
notifications.onNotification((notif) => {
notif.android.setChannelId('app-infos');
notifications.displayNotification(notif);
});
notifications.onNotificationOpened((notif) => {
const { notification: { _data: { chatroom: chatRoomId } } = {} } = notif;
if (chatRoomId) {
NavigationService.navigate('ChatRoom', { chatRoomId });
}
});
}
export default {
init,
};
With this, only go to your index.js file (or your root-file for your app, how ever it will be named) and call the init-Metod:
...
import LPFirebase from 'lib/LPFirebase';
LPFirebase.init();
hi i am using react native firebase for notifications i succeed integrating it and notifications are coming for both platform but for android heads up are not coming when app is in either foreground or background. I read all of the issues regarding this but did't got any clue.
app environment:
"react": "16.3.1",
"react-native": "0.55.3",
"react-native-firebase": "4.2.0",
firebase cloud messaging tab in console for sending message -- tried with advanced options too
so when app is in foreground app need to handle notification that's how i am doing:
componentDidMount() {
this.checkFirebase();
}
registerFbCloudMessagingListener = () => {
firebase.notifications().onNotification(notification => {
if (Platform.OS === "android") {
notification.android.setChannelId("forgroundnotification");
}
firebase.notifications().displayNotification(notification);
});
};
async checkFirebase() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
// user has permissions
this.registerFbCloudMessagingListener();
} else {
// user doesn't have permission
this.requestFbPermission();
}
}
async requestFbPermission() {
try {
let permission = await firebase.messaging().requestPermission();
if (permission) {
this.checkFirebase();
}
// User has authorised
} catch (error) {
// User has rejected permissions
}
}
start i was using mi device in that it was showing notification in only app tray then i checked in settings > my_app > notifications > show floating notification turned on then heads up started coming in that device but then i tried with one plus device in that it's not showing.
i checked all of this issues
https://github.com/invertase/react-native-firebase/issues/500
https://github.com/invertase/react-native-firebase/issues/357
https://github.com/invertase/react-native-firebase/issues/595
In oreo its not showing i think. because mi is having android N.
Please help !!! Advance thanks.
Here's how did I crack it.
First of all pushing notification from firebase console won't show notification on android. This thing I got it from the discord channel. There I've asked this question and someone suggested to setup own server like trigger notification from your backend server using firebase API and then it started working.
Also, you have to set up the channel and subscribe to that as well on android to make it work.
Here is my updated code.
Note this code is based on
"react-native-firebase": "4.2.0"
"react": "16.3.1"
"react-native": "0.55.3"
There are chances lot methods get changed in latest version and code I am giving just for reference.
Following Steps I did follow that time:
async checkFirebase() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
// user has permissions
this.registerFbCloudMessagingListener();
} else {
// user doesn't have permission
this.requestFbPermission();
}
}
async requestFbPermission() {
try {
let permission = await firebase.messaging().requestPermission();
if (permission) {
this.checkFirebase();
}
// User has authorised
} catch (error) {
// User has rejected permissions
}
}
const channelConfig = {
channelId: "channelId",
channelName: "Channel Name"
};
Subscribe to topic
Create a Channel and subscribe to it.
Check for the permissions and request for one if not there.
componentDidMount() {
firebase.messaging().subscribeToTopic("test");
const channel = new firebase.notifications.Android.Channel(
channelConfig.channelId,
channelConfig.channelName,
firebase.notifications.Android.Importance.Max
).setDescription("A natural description of the channel");
firebase.notifications().android.createChannel(channel);
this.checkFirebase();
}
I want to send a push notification from server to an ionic client and show this notification in client like a phone call (mobile device should play a sound and show 'Accept' or 'Reject' buttons with caller information). It should work if mobile app is not running or in background, that's why I decided to use FCM messages.
this.storage.get('firebase_token').then((token) => {
console.log('Orders get firebase token and call register. Token: ' + token);
this.agentService.registerPushNotifications(token, () => {
this.firebase.onNotificationOpen().subscribe((notification) => {
// How to open the app and show the page with a ringtone ??
});
});
});
How can I open the app and show the call page with a ringtone in incoming push notification? Or maybe there is a better way for this kind of feature.
What you are asking for (the same format like a phone call) isn't possible with Ionic. You can however redirect the user to a view inside the application where you ask him to take action.
Take the following example for push notification. In app.components.ts I initialize this function when the platform is ready
initializePushNotifications() {
let pushObject = this.push.init({
android: {
senderID: 'Your ID Here',
icon: 'logo'
},
ios: {
alert: true,
badge: false,
sound: true
},
windows: {}
});
if (!pushObject['error']) {
pushObject.on('registration').subscribe((data: RegistrationEventResponse) => {
// Whatever you want to do
}, err => {
console.log('Couldnt register:', err);
})
pushObject.on('notification').subscribe((data: any) => {
let self = this;
// When the user click the push notification
if (!data.additionalData.foreground) {
switch (data.additionalData.entity_type) {
case 'takeAction':
this.openView(data.additionalData.user_name, data.additionalData.id);
break;
......
}
}
});
pushObject.on('error').subscribe((e: any) => {
console.log(e.message);
});
} else {
console.error(pushObject);
}
}
See, in the pushed message we add an object under the key additionalData where you can pass whatever you want. You can pass something like entity_type with the value takeAction. When the user click it, you can open a new view and pass additional parameters like the name of the user and the id of the entity or whatever.
On this screen you can open an alert asking the user to click yes or no and based on his input you fire the correct request.
Note
I know this is different from what you were asking for but your request cannot be fulfilled using Ionic.
I have a problem with Ionic framework. I follow the guide to receive and send push notification, but something doesn't work.
When I launch the app, I press button Identify, and show my an alert that say the user ID. After, I press on register button, the phone show me an alert that contains the Device token(ANDROID). But when I go to ionic.io, in the dashboard of my app, the device token there isn't.
If I don't have the token saved on my dashboard, I can't send push notification.
Anyone can help me?
This is my controller.js:
angular.module('starter.controllers', [])
.controller('DashCtrl', function($scope, $rootScope, $ionicUser, $ionicPush) {
// Identifies a user with the Ionic User service
$scope.identifyUser = function() {
console.log('Ionic User: Identifying with Ionic User service');
var user = $ionicUser.get();
if(!user.user_id) {
// Set your user_id here, or generate a random one.
user.user_id = $ionicUser.generateGUID();
};
// Add some metadata to your user object.
angular.extend(user, {
name: 'Ionitron',
bio: 'I come from planet Ion'
});
// Identify your user with the Ionic User Service
$ionicUser.identify(user).then(function(){
$scope.identified = true;
alert('Identified user ' + user.name + '\n ID ' + user.user_id);
});
};
$rootScope.$on('$cordovaPush:tokenReceived', function(event, data) {
console.log('Got token', data.token, data.platform);
// Do something with the token
});
// Registers a device for push notifications and stores its token
$scope.pushRegister = function() {
console.log('Ionic Push: Registering user');
// Register with the Ionic Push service. All parameters are optional.
$ionicPush.register({
canShowAlert: true, //Can pushes show an alert on your screen?
canSetBadge: true, //Can pushes update app icon badges?
canPlaySound: true, //Can notifications play a sound?
canRunActionsOnWake: true, //Can run actions outside the app,
onNotification: function(notification) {
// Handle new push notifications here
// console.log(notification);
return true;
}
});
};
// Handles incoming device tokens
$rootScope.$on('$cordovaPush:tokenReceived', function(event, data) {
alert("Successfully registered token " + data.token);
console.log('Ionic Push: Got token ', data.token, data.platform);
$scope.token = data.token;
});
})
.controller('ChatsCtrl', function($scope, Chats) {
// With the new view caching in Ionic, Controllers are only called
// when they are recreated or on app start, instead of every page change.
// To listen for when this page is active (for example, to refresh data),
// listen for the $ionicView.enter event:
//
//$scope.$on('$ionicView.enter', function(e) {
//});
$scope.chats = Chats.all();
$scope.remove = function(chat) {
Chats.remove(chat);
};
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
.controller('AccountCtrl', function($scope) {
$scope.settings = {
enableFriends: true
};
});
I follow the ionic guide step-by-step
In order to access the token on ionic.io , you have to push it through:
var push = new Ionic.Push();
var user = Ionic.User.current();
var callback = function(pushToken) {
console.log('Registered token:', pushToken.token);
user.addPushToken(pushToken);
user.save(); // you NEED to call a save after you add the token
}
push.register(callback);
as mentioned in the docs.