Firebase cloud messaging notifications received but not popping up with capacitor/push-notifications - push-notification

In my Angular 13 Ionic 6 app I'm using the capacitor/push-notifications with FireBase,
In my service:
import { EventEmitter, Injectable, Output } from '#angular/core';
import {ActionPerformed, PushNotifications, PushNotificationSchema, Token} from '#capacitor/push-notifications';
import { Capacitor } from '#capacitor/core';
import { Dialogs, DialogType } from '../Utilities/dialogs';
import { get, set, remove } from './storage.service';
import { BehaviorSubject } from 'rxjs';
import { App } from '#capacitor/app';
//https://devdactic.com/push-notifications-ionic-capacitor/
#Injectable({
providedIn: 'root'
})
export class FbNotificationsService {
onFCMTokenChanged: BehaviorSubject<string> = new BehaviorSubject('');
tokenProcessed: boolean = false;
constructor(private dialogs: Dialogs) {
}
initPush() {
if (Capacitor.getPlatform() !== 'web') {
this.registerPush()
} else {
console.log('Web platform detected');
this.onFCMTokenChanged.next('web-platform');
}
}
private registerPush() {
PushNotifications.requestPermissions().then((permission) => {
if (permission.receive === 'granted') {
// Register with Apple / Google to receive push via APNS/FCM
console.log('FB Permission granted');
PushNotifications.register(); //Registers app to receive notifications
} else {
// No permission for push granted by user on iOS
console.log('FB NO permissions granted');
}
});
PushNotifications.addListener(
'registration',
(token: Token) => {
if ((token)&&(!this.tokenProcessed)) {
this.tokenProcessed = true;
console.log('My token: ' + JSON.stringify(token));
this.onFCMTokenChanged.next(token.value);
set('device-token', token.value);
}
});
PushNotifications.addListener('registrationError', (error: any) => {
console.log('Registration Error: ' + JSON.stringify(error));
this.dialogs.dialogClose('Network Error','The network signal is not sufficient at this time. Please improve connection and try again');
App.exitApp();
});
PushNotifications.addListener(
'pushNotificationReceived',
async (notification: PushNotificationSchema) => {
console.log('Push received: ' + JSON.stringify(notification));
alert('Push received: ' + JSON.stringify(notification));
//What to do next. Notification received on the device.
}
);
PushNotifications.addListener(
'pushNotificationActionPerformed',
async (notification: ActionPerformed) => {
const data = notification.notification.data;
console.log('Action performed: ' + JSON.stringify(notification.notification));
alert('Push action performed: ' + JSON.stringify(notification));
//What to do next. data holds all payload fields of the notification object
}
);
}
}
The basic guide I followed is here.
When I send a test message from the Firebase console, the alert statement I placed is poping up, meaning, the message is received by the app:
alert('Push received: ' + JSON.stringify(notification)); //WORKING
HOWEVER, there is no notification message popping up by the OS for the user to see. I do get notifications all the time from Whatapp, Emails, etc, and the "Do not disturb" setting is turned off.
This is experienced with TWO Android devices: Xiaomi Rednote 8 and Samsung Galaxy A32.
Am I missing something?

I had a similar problem before, maybe the following information could help you:
When your Android app is in the foreground, PushNotifications won't appear on the screen.
If you absolutely want a visual display of the notification, you can create a local notification when the push notification is received.

Related

this.fcm.onNotification().subscribe() not called on Android device

I have an Ionic 4 app which I have integrated with Firebase for authentication etc. I want to implement Firebase's cloud messaging so I can push messages to my app on both Android and iOS. I have done this pretty easily on iOS and I have sent a message via Postman which shows on my iPhone and I see the JSON of the message I have sent. When I try it on Android it doesn't work. Both devices receive the message but handle it very diferently.
I have read in a lot of places that you need to set the click_action to FCM_PLUGIN_ACTIVITY but when I do that the app doesn't even open on Android. When I take it out the app loads when you click the message but it doesn't show the body of the message like on iOS in my alert.
import { FCM } from '#ionic-native/fcm/ngx';
...
constructor(public platform: Platform, public fcm: FCM)
...
this.platform.ready().then(() => {
this.fcm.onNotification().subscribe(data => {
alert(JSON.stringify(data));
});
this.fcm.onTokenRefresh().subscribe(token => {
// Register your new token in your back-end if you want
// backend.registerToken(token);
});
}).catch((error) => {
this.showFailureMessage(error.message);
});
This is what I am posting off to... https://fcm.googleapis.com/fcm/send
{
"notification":{
"title":"My Title",
"body":"My Body",
"sound":"default",
"click_action":"FCM_PLUGIN_ACTIVITY"
"icon":"fcm_push_icon",
},
"data":{
"type":"Something",
},
"to":"/topics/all",
"priority":"high",
"restricted_package_name":""
}
Any help would be very much appreciated.
You seem to be missing the data.wasTapped part in your subscribe. Here, try this:
this.fcm.onNotification().subscribe(data => {
if (data.wasTapped) {
alert('Received in background');
alert(JSON.stringify(data));
} else {
alert('Received in foreground');
alert(JSON.stringify(data));
}
});

Communicating with an external service: internal server error 500

I'm trying to make a GET request to an HTTPS service ( https://broker.bronos.net ). This service is an API that communicates with a client on my LAN. I can't get it to work via functions.https.get(URL, (s,ss) => {});
Please help -- I'm very new to web development, let alone google actions.
I'm using the apiai-starter-app as the base, which functions perfectly fine until I add the line above which returns internal server error 500.
Note: I've tried before adding billing to the project and after as well. Neither work.
Edit:
using this
const https = require('https');
https.get('https://broker.bronos.net/v1/CLIENT_ID/ROOM_NAME/ACTION/PARAM', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
if (requestSource === googleAssistantRequest) {
sendGoogleResponse(JSON.parse(data).explanation); // Send simple response to user
} else {
sendResponse(JSON.parse(data).explanation); // Send simple response to user
}
});
}).on("error", (err) => {
if (requestSource === googleAssistantRequest) {
sendResponse("Error: " + err.message); // Send simple response to user
} else {
sendResponse("Error: " + err.message); // Send simple response to user
}
});
Firebase's functions have limited access to external APIs on the free tier. By upgrading to Blaze or Flame plans you will be able to make external API calls.
Enabling Firebase Blaze plan + the following code worked
const https = require('https');
https.get('https://broker.bronos.net/v1/CLIENT_ID/Living%20Room/volume/20', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
if (requestSource === googleAssistantRequest) {
sendGoogleResponse(JSON.parse(data).explanation); // Send simple response to user
} else {
sendResponse(JSON.parse(data).explanation); // Send simple response to user
}
});
}).on("error", (err) => {
if (requestSource === googleAssistantRequest) {
sendResponse("Error: " + err.message); // Send simple response to user
} else {
sendResponse("Error: " + err.message); // Send simple response to user
}
});

ServiceWorker WindowClient.navigate promise rejected

I'm using Firebase Cloud Messaging + Service worker to handle background push notifications.
When the notification (which contains some data + a URL) is clicked, I want to either:
Focus the window if it's already on the desired URL
Navigate to the URL and focus it if there is already an active tab open
Open a new window to the URL if neither of the above conditions are met
Points 1 and 3 work with the below SW code.
For some reason point #2 isn't working. The client.navigate() promise is being rejected with:
Uncaught (in promise) TypeError: Cannot navigate to URL: http://localhost:4200/tasks/-KMcCHZdQ2YKCgTA4ddd
I thought it might be due to a lack of https, but from my reading it appears as though localhost is whitelisted while developing with SW.
firebase-messaging-sw.js:
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'XXXX'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(payload => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
let notificationData = JSON.parse(payload.data.notification);
const notificationOptions = {
body: notificationData.body,
data: {
clickUrl: notificationData.clickUrl
}
};
return self.registration.showNotification(notificationData.title,
notificationOptions);
});
self.addEventListener('notificationclick', event => {
console.log('[firebase-messaging-sw.js] Notification OnClick: ', event);
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.notification.close();
let validUrls = /localhost:4200/;
let newUrl = event.notification.data.clickUrl || '';
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
event.waitUntil(
clients.matchAll({
includeUncontrolled: true,
type: 'window'
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
let client = windowClients[i];
if (validUrls.test(client.url) && 'focus' in client) {
if (endsWith(client.url, newUrl)) {
console.log('URL already open, focusing.');
return client.focus();
} else {
console.log('Navigate to URL and focus', client.url, newUrl);
return client.navigate(newUrl).then(client => client.focus());
}
}
}
if (clients.openWindow) {
console.log('Opening new window', newUrl);
return clients.openWindow(newUrl);
}
})
);
});
The vast majority of my SW code is taken from:
https://gist.github.com/vibgy/0c5f51a8c5756a5c408da214da5aa7b0
I'd recommend leaving out includeUncontrolled: true from your clients.matchAll().
The WindowClient that you're acting on might not have the current service worker as its active service worker. As per item 4 in the specification for WindowClient.navigate():
If the context object’s associated service worker client’s active
service worker is not the context object’s relevant global object’s
service worker, return a promise rejected with a TypeError.
If you can reproduce the issue when you're sure the client is currently controlled by the service worker, then there might be something else going on, but that's what I'd try as a first step.
This worked for me:
1- create an observable and make sure not to call the messaging API before it resolves.
2- register the service worker yourself, and check first if its already registered
3- call event.waitUntil(clients.claim()); in your service worker
private isMessagingInitialized$: Subject<void>;
constructor(private firebaseApp: firebase.app.App) {
navigator.serviceWorker.getRegistration('/').then(registration => {
if (registration) {
// optionally update your service worker to the latest firebase-messaging-sw.js
registration.update().then(() => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
});
}
else {
navigator.serviceWorker.register('firebase-messaging-sw.js', { scope:'/'}).then(
registration => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
}
);
}
});
this.isMessagingInitialized$.subscribe(
() => {
firebase.messaging(this.firebaseApp).usePublicVapidKey('Your public api key');
firebase.messaging(this.firebaseApp).onTokenRefresh(() => {
this.getToken().subscribe((token: string) => {
})
});
firebase.messaging(this.firebaseApp).onMessage((payload: any) => {
});
}
);
}
firebase-messaging-sw.js
self.addEventListener('notificationclick', function (event) {
event.notification.close();
switch (event.action) {
case 'close': {
break;
}
default: {
event.waitUntil(clients.claim());// this
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: "window"
}).then(function (clientList) {
...
clientList[i].navigate('you url');
...
}
}
}
}

pushPlugin notification on the server and front-en

does this get triggered again from the server containing a message?
"ecb":"window.onNotificationGCM"
I have this set up on the server
device_tokens = [], //create array for storing device tokens
retry_times = 4, //the number of times to retry sending the message if it failed
sender = new gcm.Sender('AIzaSyDpA0b2smrKyDUSaP0Cmz9hz4cQ19Rxn7U'), //create a new sender
message = new gcm.Message(); //create a new message
message.addData('title', 'Open Circles');
message.addData('message', req.query.message);
message.addData('sound', 'notification');
message.collapseKey = 'testing'; //grouping messages
message.delayWhileIdle = true; //delay sending while receiving device is offline
message.timeToLive = 3; //the number of seconds to keep the message on the server if the device is offline
device_tokens.push(val.deviceToken);
sender.send(message, device_tokens, retry_times, function(result){
console.log(result);
console.log('push sent to: ' + val.deviceToken);
});
So what I want to know is, once a server call is made will it trigger the notification on the front. What am I missing about this system?
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if (event.foreground) {
console.log('INLINE NOTIFICATION');
var my_media = new Media("/android_asset/www/" + event.soundname);
my_media.play();
} else {
if (event.coldstart) {
console.log('COLDSTART NOTIFICATION');
} else {
console.log('BACKGROUND NOTIFICATION');
}
}
navigator.notification.alert(event.payload.message);
console.log('MESSAGE -> MSG: ' + event.payload.message);
//Only works for GCM
console.log('MESSAGE -> MSGCNT: ' + event.payload.msgcnt);
//Only works on Amazon Fire OS
console.log('MESSAGE -> TIME: ' + event.payload.timeStamp);
break;
case 'error':
console.log('ERROR -> MSG:' + event.msg);
break;
default:
console.log('EVENT -> Unknown, an event was received and we do not know what it is');
break;
}
};
return {
register: function () {
var q = $q.defer();
if(ionic.Platform.isAndroid()){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"346007849782",
"ecb":"window.onNotificationGCM"
}
);
}else{
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"window.onNotificationAPN"
}
);
}
return q.promise;
}
}
update. Eventually my server spit back this: TypeError: Cannot read property 'processIncomingMessage' of undefined
It seems my google ID was not working. I created a new one and now it's sending push requests.

Is there any way for sending push notifications without appcelaretor cloud services?

I already know how to send push notifications in Titanium with Alloy, the way I do is:
// Require the module
var CloudPush = require('ti.cloudpush');
var deviceToken = null;
// Initialize the module
CloudPush.retrieveDeviceToken({
success: deviceTokenSuccess,
error: deviceTokenError
});
// Enable push notifications for this device
// Save the device token for subsequent API calls
function deviceTokenSuccess(e) {
deviceToken = e.deviceToken;
// alert("--->" + deviceToken);
subscribeToChannel();
}
function deviceTokenError(e) {
alert('Failed to register for push notifications! ' + e.error);
}
// Process incoming push notifications
CloudPush.addEventListener('callback', function (evt) {
alert("Notification received: " + evt.payload);
});
// For this example to work, you need to get the device token. See the previous section.
// You also need an ACS user account.
// Require in the Cloud module
var Cloud = require("ti.cloud");
function loginUser(){
// Log in to ACS
Cloud.Users.login({
login: 'example',
password: 'example'
}, function (e) {
if (e.success) {
alert('Login successful');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
function subscribeToChannel(){
// Subscribe the user and device to the 'test' channel
// Specify the push type as either 'android' for Android or 'ios' for iOS
// Check if logged in:
Cloud.PushNotifications.subscribe({
channel: 'test',
//device_token: 'APA91bHRjGoZLCYKwn-XcCtNLETuf-KRKfT4sMgVE4KgXQgInYfZuYTNrZC7FUMugLs0idzzqtLytrvVJjVzYBzQoc7Q81hEerq0O2vww_tV8mACuUfAi0JRvs7LoufnQZpYLZrb_1rlUsIOEMsPxDs9b_pIRJF5rw',
device_token:deviceToken,
type: Ti.Platform.name == 'android' ? 'android' : 'ios'
}, function (e) {
if (e.success) {
alert('Subscribed');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
function unsubscribeToChannel (){
// Unsubscribes the user and device from the 'test' channel
Cloud.PushNotifications.unsubscribe({
channel: 'test',
device_token: deviceToken
}, function (e) {
if (e.success) {
alert('Unsubscribed');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
loginUser();
However this way is only for sending push notification through https://cloud.appcelerator.com/ such a manually because it is needed you write the alert and push the button in that backend site.
So my question: Is there any way for sending push notification in Titanium from an own server in an "automatically" way?
Thanks in advance for any help.
Yes, it is possible.
How to obtain a device token for push notifications is described in the Titanium docs, here.
To send notifications you have to send the token to your server. The server then sends your notification to Apple Push Notification Services (APNS). See Apple docs. That's not "automatic" but it's a simple task for PHP or any other language - you can find a lot of scripts.
You can also schedule local notifications which might come in handy depending on your case.

Resources