How to bring app to foreground from background in Ionic 2? - api

Is there any possible way to bring app from background mode in to foreground when an event happens like notification arrive? like WhatsApp and Skype that bring into foreground when a voice call or video call arrive.
Update:
public subscribeToPushNotificationEvents(): void {
// Handle token refresh
this.firebase.onTokenRefresh().subscribe(
token => {
//console.log(`The new token is ${token}`);
this.saveToken(token);
},
error => {
console.error('Error refreshing token', error);
});
// Handle incoming notifications
this.firebase.onNotificationOpen().subscribe(
(notification: NotificationModel) => {
this.backgroundMode.moveToForeground();
let notificationAlert = this.alertCtrl.create({
title: notification.title,
message: notification.body,
buttons: ['Ok']
});
notificationAlert.present();
}

As far as I know, your only option is this plugin: http://ionicframework.com/docs/native/background-mode/

Related

Can't get click_action to work on FCM notifications with web app / PWA

I'm trying to get my "click_action" to take users to specific URLs on notifications that I'm sending to clients, but whatever I do it either does nothing (desktop) or just opens the PWA (android). The messages are coming through fine (checked in Chrome console) but clicking just doesn't seem to work.
I have the following in my service worker, cribbed from various places including other answers provided on this site:
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-messaging.js');
// importScripts('/__/firebase/init.js');
/* An empty service worker! */
self.addEventListener('fetch', function(event) {
/* An empty fetch handler! */
});
var firebaseConfig = {
//REDACTED
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
notificationTitle = payload.notification.title;
notificationOptions = {
body: payload.notification.body,
icon: payload.notification.icon,
click_action: payload.notification.click_action
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
self.addEventListener('notificationclick', function(event) {
let url = event.notification.click_action;
// I've also added a data.click_action field in my JSON notification, and have tried using that
// instead, but that didn't work either
console.log('On notification click: ', event.notification.tag);
event.notification.close(); // Android needs explicit close.
event.waitUntil(
clients.matchAll({ includeUncontrolled: true, type: 'window' }).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
self.onnotificationclick = function(event) {
let url = event.notification.click_action;
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({ includeUncontrolled: true, type: 'window' }).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == url && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow(url);
}));
};
The notifications come through fine on both android (installed PWA) and chrome, and the message payload in the developer console is well formatted and received fine. In the message I'm sending from the server I have a URL with a custom parameter on the end (e.g. https://[domain]/list.php?userid=123) but, as above, clicking on the notification doesn't do anything on windows/chrome, and on the android it opens the PWA successfully but then doesn't go to the URL in the payload, it just goes to wherever the PWA was when last open. The "userid" changes depending on the message trigger.
Sample JSON of message payload:
{data: {…}, from: "xxx", priority: "high", notification: {…}, collapse_key: "do_not_collapse"}
collapse_key: "do_not_collapse"
data: {gcm.notification.badge: "[logo URL]", click_action: "https://[URL]/list.php?userid=33"}
from: "xxx"
notification:
body: "'5' has just been added"
click_action: "https://[URL]/list.php?userid=33"
icon: "https://[logo URL]"
title: "alert "
I also saw something about "webpush": { "fcm_options": { "link": "https://dummypage.com"}} on https://firebase.google.com/docs/cloud-messaging/js/receive but couldn't figure out if that was relevant or needed also.
Am very surprised just providing a URL in the click_action doesn't seem to just do that action when you click the notificaiton! Is anything needed in the service worker at all?!?!
Could one of the problems be that the PWA doesn't update the SW regularly, and so if my code above should work (a big if!) then i just need to wait for the SW to update on the installed android app? If so, is there a way to speed up its updating?!?
Thanks so much in advance for any assistance. Am tying myself in knots here!
I spent a lot of time looking for a solution for the same problem. Maybe this can help :
if you send notification with firebase messaging, you can use webpush field. firebase messaging client library execute self.registration.showNotification() ... No more need messaging.onBackgroundMessage in your service worker.
// firebabse-coud-function.js
app.messaging().send({
webpush: {
notification: {
title: notification?.title || "Default title",
icon: notification?.icon || "/icon.png",
badge: notification?.icon || "/icon.png",
},
fcmOptions: {
link: `${BASE_URL || ""}${notification?.clickAction || "/"}`,
}
},
data: {
userID: notification.userID,
link: notification?.clickAction || "/",
},
topic
});
Most importantly, in your service worker add a 'notificationclick' event listener before calling firebase.messaging()
so my service worker looks like:
// firebase-messaging-sw.js
// ...
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
// ...
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
// received others messages
})
For me, clicking on the event does not go to the correct url. So i add this:
// background client - service worker
const channel = new BroadcastChannel('sw-messages');
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
channel.postMessage({
type: 'notification_clicked',
data: {
title: event.notification.title,
clickAction: url
}
});
})
// foreground client
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener("message", function (event) {
// go the page
})
I hope this helps someone.
This question and other answers seems to be related to the legacy FCM API, not the v1.
In those case, I needed the SW to open any url sent by FCM, which is by default not possible because host differs (see here).
Also, the notification object as changed, and the url for the webpush config is there now: event.notification.data.FCM_MSG.notification.click_action
So adapting others answers to get the correct field and open the url by only editing the firebase-messaging-sw.js:
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
...
})
self.addEventListener('notificationclick', function(event) {
event.notification.close();
// fcp_options.link field from the FCM backend service goes there, but as the host differ, it not handled by Firebase JS Client sdk, so custom handling
if (event.notification && event.notification.data && event.notification.data.FCM_MSG && event.notification.data.FCM_MSG.notification) {
const url = event.notification.data.FCM_MSG.notification.click_action;
event.waitUntil(
self.clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (self.clients.openWindow) {
console.log("open window")
return self.clients.openWindow(url);
}
})
)
}
}, false);
const messaging = firebase.messaging();
(register the addEventListener before initializing messaging)
Just add addeventlistner notification click event before calling firebase.messaging()
Everything will work fine.
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-messaging.js');
self.onnotificationclick = function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/index' && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow('/index');
}));
};
var firebaseConfig = {
apiKey: "xcxcxcxcxcxc",
authDomain: "xcxcxc.firebaseapp.com",
projectId: "fdsfdsdfdf",
storageBucket: "dfsdfs",
messagingSenderId: "sdfsdfsdf",
appId: "sdfsdfsdfsdfsdfsdf"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

how to set small Icon when pushing message in nodejs

I'm usnig fcm for notification in nodejs. So I made this And when I push message from nodejs to android, I got message well. When the app is running, I can get the small Icon image. But when the app is not running, I got the notification with no image. how can I get the push message with image when the app is in background? here is my nodejs code
let pushMsg = req.body.pushMsg;
let groupName = req.body.groupName;
console.log(groupName)
var condition = "'"+groupName+"' in topics"; //"'all' in topics || 'industry-tech' in topics";
var message = {
notification: {
title: 'schedule updated',
body: pushMsg,
image:'./upload/haiilogo.png'
},
condition: condition
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
I solved. I added manifest to default icon like this
<meta-data android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/haiilogo" />

Set up listeners only after permission is verified

I am using react-native-notifications - Version 2.1.7 library to receive notifications in a react-native mobile app. I don't want to set up the notification-related listeners until the user has provided permission to receive notifications.
Q1. The documentation says that it is highly recommended to keep listeners registration at global scope rather than at screen scope. What problems should I expect if I set up listeners at a screen, at which the user is asked to provide permission?
Q2. The device token listener NotificationsAndroid.setRegistrationTokenUpdateListener() does NOT seem to work if it is inside a promise. What am I missing here? Please, see my code below.
// This function is called when the user clicks on the button "Provide permission to receive notifications."
const _requestPermissionNotification = async () => {
let hasPermission = false;
try {
hasPermission = await NotificationsAndroid.isRegisteredForRemoteNotifications();
}
catch (error) {
Alert.alert(
"Notification",
"To utilise the full functionality of this app, Permission to receive notifications is required.",
[{ text: "Ok." }]
);
} // end of: try/catch
if (hasPermission) {
// A. Register Token
// THIS LISTENER DOES NOT SEEM TO WORK UNLESS IT IS SET UP OUTSIDE THE COMPONENT!
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
console.log("PermissionsScreen - setRegistrationTokenUpdateListener - deviceToken:", deviceToken);
});
// B. Pending Notifications
PendingNotifications.getInitialNotification()
.then((notification) => {
console.log("PermissionsScreen - getInitialNotification - notification:", notification);
})
.catch((err) => console.error("getInitialNotifiation failed", err));
// C. Notification Opened
NotificationsAndroid.setNotificationOpenedListener((notification) => {
console.log("PermissionsScreen - setNotificationOpenedListener - :data", notification.getData());
});
// D.a Notification Received
NotificationsAndroid.setNotificationReceivedListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedListener - data:", notification.getData());
});
// D.b Notification Received "IN FOREGROUND"
NotificationsAndroid.setNotificationReceivedInForegroundListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedInForegroundListener (foreground)", notification.getData());
});
} // end of: if()
}; // end of: _requestPermissionNotification()
It seems that version 3.1.1 of React-Native-Notifications does not have these limitations anymore.
The following code, which uses the new commands, can be used inside a promise and inside a component.
// Step A.1: Register this app to receive notifications.
Notifications.registerRemoteNotifications();
// Step A.2: Get the device token
Notifications.events().registerRemoteNotificationsRegistered( (event) => {
console.log("deviceToken:", event.deviceToken);
});

Send push notification using Expo

I am trying to send push notification using Expo, and I receive it. But there is no vibrate or sound and no pop up as well on my device. I am using Galaxy S9 with Android 9. I have not tried on Iphone yet.
Push notification is sent by nodejs and the user who installed the app will receive the push notification. User expo token is saved in firebase database. I succeed to save and fetch the token.
Below is from expo app
class App extends Component {
componentWillMount() {
firebase.initializeApp(config);
this.registerForPushNotificationsAsync();
}
async registerForPushNotificationsAsync(){
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (Platform.OS === 'android') {
Notifications.createChannelAndroidAsync('chat-messages', {
name: 'Chat messages',
sound: true,
priority: 'high', // was max
vibrate: [0, 250, 250, 250],
});
}
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
Below is from nodejs server-side
function sendMessage(to, title, body) {
const expo = new Expo();
let messages = [];
messages.push({
to, // Expo user token
body,
data: { withSome: 'data' },
ios: {
sound: true
},
android: {
"channelId": "chat-messages" //and this
}
})
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
(async () => {
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
} catch (error) {
console.error(error);
}
}
})();
}
Also could we redirect to web page when user click the push notification?
I see three problems on your backend code ( expo push notification docs for reference https://docs.expo.io/versions/latest/guides/push-notifications/):
According to the docs, there should be no ios or android properties on the request body;
sound should be either 'default' or null, instead of true;
You created the notification channel on the device, but when you send the notification, you forgot to tell which channel you are sending to.
All that said, your code that calls the expo push notifications api should look something like this:
messages.push({
to, // Expo user token
title: 'some title', //it is good practice to send title, and it will look better
body,
data: { withSome: 'data' },
priority: 'high', //to make sure notification is delivered as fast as possible. see documentation for more details
sound: true, //for iOS devices and android below 8.0
channelId: 'chat-messages', //for devices on android 8.0 and above
})
I hope this helps.

Correct setup with react-native-fcm

I feel like the docs at react-native-fcm are a bit of a mess and I am having a hard time figuring this out.
I currently have a production app and my android users are telling me they are not receiving notifications for events where they should be. So this is stressing me out a lot right now. On iOS everything seems fine.
In the react-native-fcm example app you can see the following:
//FCM.createNotificationChannel is mandatory for Android targeting >=8. Otherwise you won't see any notification
componentDidMount() {
FCM.createNotificationChannel({
id: 'default',
name: 'Default',
description: 'used for example',
priority: 'high'
})
}
Do I need to call FCM.createNotificationChannel()?? I mainly use remote notifications so is this relevant in any way?
Here is MY setup:
import FCM, {
FCMEvent,
NotificationType,
RemoteNotificationResult,
WillPresentNotificationResult,
} from 'react-native-fcm';
FCM.on(FCMEvent.Notification, async (notif) => {
// there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
FCM.presentLocalNotification({
id: "new_message", // (optional for instant notification)
title: notif.fcm.title, // as FCM payload
body: notif.fcm.body, // as FCM payload (required)
sound: "default", // as FCM payload
priority: "high", // as FCM payload
click_action: "com.myapp.MyCategory", // as FCM payload - this is used as category identifier on iOS.
badge: 10, // as FCM payload IOS only, set 0 to clear badges
number: 10, // Android only
ticker: "My Notification Ticker", // Android only
auto_cancel: true, // Android only (default true)
large_icon: "ic_launcher", // Android only
icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap
color: "blue", // Android only
vibrate: 300, // Android only default: 300, no vibration if you pass 0
wake_screen: true, // Android only, wake up screen when notification arrives
group: "group", // Android only
picture: "https://google.png", // Android only bigPicture style
ongoing: false, // Android only
my_custom_data:'my_custom_field_value', // extra data you want to throw
lights: true, // Android only, LED blinking (default false)
});
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) => {
try {
const { currentUser } = firebase.auth();
let updates = {};
updates[`/allUsers/serviceUsers/${currentUser.uid}/userInfo/fcmToken`] = token;
return firebase.database().ref().update(updates).catch(err => console.log('fcm refresh error', err))
} catch (e) {
console.log('couldnt update fcm refresh token', e)
}
});
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
class App extends Component {
componentWillMount() {
let config = {configgg}
!firebase.apps.length ? firebase.initializeApp(config) : firebase.app();
FCM.requestPermissions();
}
render() {
return (
<Provider store={store}>
<Router/>
</Provider>
);
}
}
export default App;
I mainly use remote notifications and it is critical for my app for it to work. Is there anything I am missing in my setup?
Any hint or suggestions will help me out a lot! Thanks!
EDIT:
Found this in adb logcat when receiving a notification (that did not show up)
NotificationService: No Channel found for pkg=com.lisdoworker, channelId=null, id=0, tag=GCM-Notification:9015992, opPkg=com.lisdoworker, callingUid=10487, userId=0, incomingUserId=0, notificationUid=10487, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x10 color=0x00000000 vis=PRIVATE)
Does this have to do with FCM.createNotificationChannel()??
Yeah, apparently createNotificationChannel was added in version 16 to support Android 8 and it is barely documented.