ionic push notification when app is in foreground - ionic-framework

I am making a ionic 3 app. I want notifications to appear even when app is in foreground. I have tried using FCM Plugin I'm getting notifications only when app is in background.
Home.ts
import { AngularFireDatabase } from 'angularfire2/database';
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import firebase from 'firebase';
declare var FCMPlugin;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
firestore = firebase.database().ref('/pushtokens');
firemsg = firebase.database().ref('/messages');
constructor(public navCtrl: NavController,public afd:AngularFireDatabase) {
this.tokensetup().then((token)=>{
this.storeToken(token);
})
}
ionViewDidLoad() {
FCMPlugin.onNotification(function (data) {
if (data.wasTapped) {
//Notification was received on device tray and tapped by the user.
alert(JSON.stringify(data));
} else {
//Notification was received in foreground. Maybe the user needs to be notified.
alert(JSON.stringify(data));
}
});
FCMPlugin.onTokenRefresh(function (token) {
alert(token);
});
}
tokensetup(){
var promise = new Promise((resolve,reject)=>{
FCMPlugin.getToken(function(token){
resolve(token);
},(err)=>{
reject(err);
});
})
return promise;
}
storeToken(token){
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: token
}).then(()=>{
alert('Token stored')
}).catch(()=>{
alert('Token not stored');
})
// this.afd.list(this.firemsg).push({
// sendername:'adirzoari',
// message: 'hello for checking'
// }).then(()=>{
// alert('Message stored');
// }).catch(()=>{
// alert('message not stored');
// })
}
}
the function cloud for notifications
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var wrotedata;
exports.Pushtrigger = functions.database.ref('/messages/{messageId}').onWrite((event) => {
wrotedata = event.data.val();
admin.database().ref('/pushtokens').orderByChild('uid').once('value').then((alltokens) => {
var rawtokens = alltokens.val();
var tokens = [];
processtokens(rawtokens).then((processedtokens) => {
for (var token of processedtokens) {
tokens.push(token.devtoken);
}
var payload = {
"notification":{
"title":"From" + wrotedata.sendername,
"body":"Msg" + wrotedata.message,
"sound":"default",
},
"data":{
"sendername":wrotedata.sendername,
"message":wrotedata.message
}
}
return admin.messaging().sendToDevice(tokens, payload).then((response) => {
console.log('Pushed notifications');
}).catch((err) => {
console.log(err);
})
})
})
})
function processtokens(rawtokens) {
var promise = new Promise((resolve, reject) => {
var processedtokens = []
for (var token in rawtokens) {
processedtokens.push(rawtokens[token]);
}
resolve(processedtokens);
})
return promise;
}
it works only when the app in the background. but when i exit from the app and it's not in the background I don't get any notification.

You need to edit the FCM Plugin files. I found the solution only for android now.
I use https://github.com/fechanique/cordova-plugin-fcm this FCM plugin for android and ios in cordova.
You need to edit file MyFirebaseMessagingService.java line 53(line no be may be differ).
In this file there is a method onMessageReceived at the end of the method there is a line which is commented, this line calling an another method i.e. sendNotification(....).
sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), data);
You have to uncomment this line and change last parameter from remoteMessage.getData() to data (data variable is already there in the code).
And comment this line FCMPlugin.sendPushPayload( data );
Now you are good to go. Now you are able to receive notification even when app is opened (foreground), you will receive the banner (floating) notifications.
If you found anything for IOS please let me know!!!

I am using firebase plugin for ionic 3.
There is a check if notification data contain "notification_foreground" or not and save it in variable foregroundNotification.
if(data.containsKey("notification_foreground")){
foregroundNotification = true;
}
then it create showNotification variable which decide if we need to show notification or not and pass this to the sendMessage (show notification function).
if (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title) || (data != null && !data.isEmpty())) {
boolean showNotification = (FirebasePlugin.inBackground() || !FirebasePlugin.hasNotificationsCallback() || foregroundNotification) && (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title));
sendMessage(data, messageType, id, title, body, showNotification, sound, vibrate, light, color, icon, channelId, priority, visibility);
}
your payload should contain notification_foreground, notification_title and notification_body.

Related

PushNotifications stop working after installing Community/FCM plugin

I am currently using ionic capacitor PushNotifications for Firebase cloud messaging. By the help of this I can get tokens.
Now I want to send notifications to all app users so I decided to use topics.
For this, I installed community/FCM plugin.
capacitor-community/fcm
I also made suggested changes in the MainActivity.java file, no error during app build.
But after installation this FCM plugin, PushNotifications(token) stop working.
Even below code starts always retuning false.
if (isPushNotificationsAvailable) {
this.initPushNotifications();
}
Here is my MainActivity.java file:-
package io.ionic.starter;
import com.getcapacitor.community.fcm.FCMPlugin;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Plugin;
import java.util.ArrayList;
public class MainActivity extends BridgeActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initializes the Bridge
this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
// Additional plugins you've installed go here
// Ex: add(TotallyAwesomePlugin.class);
add(FCMPlugin.class);
}});
}
}
Here my fcm.service.ts File:-
import { Component, Injectable } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { Router } from '#angular/router';
import { Storage } from '#ionic/storage';
import {
ActionPerformed,
PushNotificationSchema,
PushNotifications,
Token
} from '#capacitor/push-notifications';
// import { FCM } from '#capacitor-community/fcm';
const isPushNotificationsAvailable = Capacitor.isPluginAvailable('PushNotifications');
#Injectable({
providedIn: 'root'
})
export class FcmService {
public topicName = 'project';
constructor(
private router: Router,
private storage: Storage
) { }
initPush() {
//This Plugin is not available on web
if (isPushNotificationsAvailable) {
this.initPushNotifications();
}
}
// Request permission to use push notifications
// iOS will prompt user and return if they granted permission or not
// Android will just grant without prompting
private initPushNotifications() {
//console.log('Initializing HomePage');
PushNotifications.requestPermissions().then(result => {
if (result.receive === 'granted') {
// Register with Apple / Google to receive push via APNS/FCM
PushNotifications.register();
} else {
// Show some error
}
});
PushNotifications.addListener('registration', (token: Token) => {
alert('Push registration success, token: ' + token.value);
//Store Devive Token in session variable
this.storage.set("device_token", token.value);
// FCM.getToken()
// .then((r) => alert(`Token ${r.token}`))
// .catch((err) => console.log(err));
// now you can subscribe to a specific topic
// FCM.subscribeTo({ topic: this.topicName })
// .then((r) => alert(`subscribed to topic`))
// .catch((err) => console.log(err));
});
PushNotifications.addListener('registrationError', (error: any) => {
//alert('Error on registration: ' + JSON.stringify(error));
});
PushNotifications.addListener(
'pushNotificationReceived',
(notification: PushNotificationSchema) => {
//alert('Push received: ' + JSON.stringify(notification));
},
);
PushNotifications.addListener(
'pushNotificationActionPerformed',
(notification: ActionPerformed) => {
// alert('Push action performed: ' + JSON.stringify(notification));
const data = notification.notification.data;
//console.log('Action performed: ' + JSON.stringify(notification.notification));
if (data.detailsId) {
this.router.navigate(['/bid-project', data.detailsId]);
}
},
);
}
//Reset all Badge count
// resetBadgeCount() {
// PushNotifications.removeAllDeliveredNotifications();
// }
// move to fcm demo
// subscribeTo() {
// PushNotifications.register()
// .then((_) => {
// FCM.subscribeTo({ topic: this.topicName })
// .then((r) => alert(`subscribed to topic ${this.topicName}`))
// .catch((err) => console.log(err));
// })
// .catch((err) => alert(JSON.stringify(err)));
// }
// unsubscribeFrom() {
// FCM.unsubscribeFrom({ topic: this.topicName })
// .then((r) => alert(`unsubscribed from topic ${this.topicName}`))
// .catch((err) => console.log(err));
// }
}
After removing community plugin token generation started working as before.
Please tell me what wrong is with my code.

Events in Ionic v4

I'm using Ioni v4Beta and I'm traying to update the sidemenu when the user is login.
I search but the usual solution is use Events:
Ionic 3 refresh side menu after login
https://ionicframework.com/docs/api/util/Events/
But in the new version I don't find it, and I don't know how to do it
https://beta.ionicframework.com/docs/api
Thanks a lot, but I finally find how to import it:
import { Events } from '#ionic/angular';
Example on how to do it with subjects:
export const someEvent:Subject = new Subject();
export class ReceivingClass implements OnDestroy, OnInit
{
private someEventSubscription:Subscription;
public OnInit():void{
someEventSubscription = someEvent.subscribe((data) => console.log(data);
}
public onDestroy():void{
someEvent.unsubscribe();
}
}
export class SendingClass implements OnInit
{
public OnInit():void{
setTimeout(() => {
someEvent.next('hi');
}, 500);
}
}
Are you aware that Ionic v4 events will be deprecated soon?
I was also trying to update the sidemenu when a user logs in as well, so i tried using: import { Events } from '#ionic/angular';
However I got a warning referring me to this link https://angular.io/guide/observables#basic-usage-and-terms which I failed to follow because am not that familiar with observables.
After much research I found that I can still use events but I had to import them from angular's router directive.
This was my code before:
/* import was */
import { Events } from '#ionic/angular';
import { Storage } from '#ionic/storage';//ignore this import if doesn't apply to your code
/* inside the class */
constructor(
private events: Events,
private storage: Storage
) {
this.events.subscribe("updateMenu", () => {
this.storage.ready().then(() => {
this.storage.get("userLoginInfo").then((userData) => {
if (userData != null) {
console.log("User logged in.");
let user = userData.user;
console.log(user);
}
else {
console.log("No user found.");
let user = {};
}
}).catch((error)=>{
console.log(error);
});
}).catch((error)=>{
console.log(error);
});
});
}
changes i made that actually got my code working and deprecation warning gone:
/* import is now */
import { Router,RouterEvent } from '#angular/router';
import { Storage } from '#ionic/storage';//ignore this import if it does't apply to your code
Rest of code
constructor(
public router: Router,
public storage: Storage
){
this.router.events.subscribe((event: RouterEvent) => {
this.storage.ready().then(() => {
this.storage.get("userLoginInfo").then((userData) => {
if (userData != null) {
/*console.log("User logged in.");*/
let user = userData.user;
/*console.log(this.user);*/
}
else {
/*console.log("No user found.");*/
let user = {};
}
}).catch((error)=>{
console.log(error);
});
}).catch((error)=>{
console.log(error);
});
});
}
I got the idea after seeing this https://meumobi.github.io/ionic/2018/11/13/side-menu-tabs-login-page-ionic4.html. I hope my answer can be useful.
Steps to resolve the issue
import events in login page and in sidemenu view
In login page, after login success do your logic to publish the events.
for eg:
this.authService.doLogin(payload).subscribe((response) => {
if (response.status) {
this.storage.set('IS_LOGGED_IN', true);
this.events.publish('user:login');
}
}, (error) => {
console.log(error);
});
In sidemenu view, create a listener to watch the events 'user:login'
for eg:
this.menus = [];
// subscribe events
this.events.subscribe('user:login', () => {
// DO YOUR LOGIC TO SET THE SIDE MENU
this.setSidemenu();
});
// check whether the user is logged in or not
checkIsUserloggedIn() {
let isLoggedIn = false;
if (this.storage.get('IS_LOGGED_IN') == '' ||
this.storage.get('IS_LOGGED_IN') == null ||
this.storage.get('IS_LOGGED_IN') == undefined) {
isLoggedIn = false;
} else {
isLoggedIn = true;
}
return isLoggedIn;
}
// to set your sidemenus
setSidemenu() {
let isUserLoggedIn = this.checkIsUserloggedIn();
if(isUserLoggedIn) {
this.menus = ['Home', 'Aboutus', 'Contactus', 'My Profile', 'Logout'];
} else {
this.menus = ['Login', 'Home', 'Aboutus', 'Contactus'];
}
}

WebRTC with PeerJS remote video not showing on Android

I'm using Ionic3 to build an android videochat application.
The videochat works perfectly between two tabs on my browser, but only shows the local video on my android device (the remote video being blank).
I'm using PeerJS for the peer-to-peer connection in my index.html:
I'm using the stunServer {url: "stun:stun.l.google.com:19302"} for the connection.
I'm using the functions shown on the home page: http://peerjs.com/
My config service:
import {Injectable} from '#angular/core';
#Injectable()
export class WebRTCConfig {
peerServerPort: number = 9000;
key:string = '<my peer id>';
stun: string = 'stun.l.google.com:19302';
stunServer = {
url: 'stun:' + this.stun
};
getPeerJSOption() {
return {
// Set API key for cloud server (you don't need this if you're running your own.
key: this.key,
// Set highest debug level (log everything!).
debug: 3,
// Set it to false because of:
// > PeerJS: ERROR Error: The cloud server currently does not support HTTPS.
// > Please run your own PeerServer to use HTTPS.
secure: false,
config: {
iceServers: [
this.stunServer/*,
this.turnServer*/
]
}
};
}
/**********************/
audio: boolean = true;
video: boolean = false;
getMediaStreamConstraints(): MediaStreamConstraints {
return <MediaStreamConstraints> {
audio: this.audio,
video: this.video
}
}
}
Snippet of my Peer WebRTC service:
createPeer(userId: string = '') {
// Create the Peer object where we create and receive connections.
this._peer = new Peer(/*userId,*/ this.config.getPeerJSOption());
setTimeout(()=> {
console.log(this._peer.id);
this.myid = this._peer.id;
}, 3000)
}
myCallId() {
return this.myid;
}
answer(call) {
call.answer(this._localStream);
this._step2(call);
}
init(myEl: HTMLMediaElement, otherEl: HTMLMediaElement, onCalling: Function) {
this.myEl = myEl;
this.otherEl = otherEl;
this.onCalling = onCalling;
// Receiving a call
this._peer.on('call', (call) => {
// Answer the call automatically (instead of prompting user) for demo purposes
this.answer(call);
});
this._peer.on('error', (err) => {
console.log(err.message);
// Return to step 2 if error occurs
if (this.onCalling) {
this.onCalling();
}
// this._step2();
});
this._step1();
}
call(otherUserId: string) {
// Initiate a call!
var call = this._peer.call(otherUserId, this._localStream);
this._step2(call);
}
endCall() {
this._existingCall.close();
// this._step2();
if (this.onCalling) {
this.onCalling();
}
}
private _step1() {
// Get audio/video stream
navigator.getUserMedia({ audio: true, video: true }, (stream) => {
// Set your video displays
this.myEl.src = URL.createObjectURL(stream);
this._localStream = stream;
// this._step2();
if (this.onCalling) {
this.onCalling();
}
}, (error) => {
console.log(error);
});
}
private _step2(call) {
// Hang up on an existing call if present
if (this._existingCall) {
this._existingCall.close();
}
// Wait for stream on the call, then set peer video display
call.on('stream', (stream) => {
this.otherEl.src = URL.createObjectURL(stream);
});
// UI stuff
this._existingCall = call;
// $('#their-id').text(call.peer);
call.on('close', () => {
// this._step2();
if (this.onCalling) {
this.onCalling();
}
});
}
In my chat.ts, I use this to call the function from the peer webrtc service:
call() {
this.webRTCService.call(this.calleeId);
}
It's likely to be a permission problem. You need to grant it permission to use the camera.
Camera Permission - Your application must request permission to use a
device camera.
<uses-permission android:name="android.permission.CAMERA" />
See
https://developer.android.com/guide/topics/media/camera.html

Ionic 2: push notification on click

A notification appears, but upon clicking them, they only open the application again. What I want is upon clicking the notification, it opens a specific item.
In Laravel, I am using the brozot/Laravel-FCM package for Firebase Cloud Messaging (FCM) to send notifications, and on the other end, I'm using Ionic push notifications to receive and display notifications in the notification tray.
If I don't use setClickAction() on Laravel, the Ionic application opens upon clicking the notification, but if I set setClickAction(), then nothing happens. The notification merely disappears.
Laravel-code:
$notificationBuilder = new PayloadNotificationBuilder('my title');
$notificationBuilder->setBody('Hello world')
->setSound('default')
->setClickAction('window.doSomething');
$notification = $notificationBuilder->build();
Ionic 2 framework sample:
import { Component, ViewChild } from '#angular/core';
import { Platform, Nav, MenuController, ModalController, Events, AlertController } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { Push, PushObject, PushOptions } from '#ionic-native/push';
import { Storage } from '#ionic/storage';
import {
SearchPage
} from '../pages/pages';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage: any = SearchPage;
constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
private menu: MenuController,
private modalCtrl: ModalController,
private events: Events,
private push: Push,
private alertCtrl: AlertController,
private storage: Storage
) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
this.pushSetup();
}
pushSetup() {
const options: PushOptions = {
android: {
senderID: 'xxxxxxxxxxx',
forceShow: true
},
ios: {
senderID: 'xxxxxxxxxxx',
alert: 'true',
badge: true,
sound: 'true'
},
windows: {},
browser: {
pushServiceURL: 'http://push.api.phonegap.com/v1/push'
}
};
const pushObject: PushObject = this.push.init(options);
pushObject.on('notification').subscribe((notification: any) => {
});
pushObject.on('registration').subscribe((registration: any) => {
alert(registration.id);
});
pushObject.on('error').subscribe(error => alert('Error with Push plugin' + error));
}
}
(<any>window).doSomething = function () {
alert('doSomething called');
}
What am I missing?
There are these steps that need to be done for general One-Signal push notification to be implemented
Create a OneSignal Account
Add a New APP in the One Signal , configure for Android first (you can target for any platform but i'm focussing on Android as of now) .you need to get the Google Server Key and Google Project Id.
You can get the Above keys from the Firebase using this Steps
Now we are done with Configuring the OneSignal Account, now integrate with the ionic using the cordova plugin
In Ionic2 :
OneSignal.startInit(//google Server Key, //Google ProjectId);
OneSignal.inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
OneSignal.setSubscription(true);
OneSignal.handleNotificationReceived().subscribe(() => {
// handle received here how you wish.
// this.goToReleatedPage(data.Key, data.Value);
});
OneSignal.handleNotificationOpened().subscribe((data: any) => {
//console.log('MyData'+ JSON.stringify(data.additionalData));
this.parseObject(data);
});
OneSignal.endInit();
ParsingObject in Ionic
public parseObject(obj) {
for (var key in obj) {
this.goToReleatedPage(key, obj[key]);
if (obj[key] instanceof Object) {
this.parseObject(obj[key]);
}
}
}
goToReleatedPage Method
public goToReleatedPage(Key, Value) {
//console.log("Pagename"+" " + Key + "ID" +" " + Value);
if (Key === 'xxxx') {
this.navCtrl.push(xxxPage, {
id: Value
});
} else if (Key === 'Foo') {
this.navCtrl.push(foosPage, {
id: Value,
});
} else if (Key === 'bar') {
this.navCtrl.push(barPage, {
id: Value
});
}
}
While sending the Message from OneSignal , you need to specify which page you need to open and you want to pass Id as follows

one signal additional data in ionic 2/3

I'm trying to work with one signal plugin in my ionic 2 app
I've installed Onesignal and it was working fine,but i don't know how to work with handleNotificationOpened function
there is no document at all (nothing was found)
this is my code:
this.oneSignal.handleNotificationReceived().subscribe((msg) => {
// o something when notification is received
});
but I have no idea how to use msg for getting data.
any help? link?
tank you
Here is how i redirect user to related page when app launch from notification.
app.component.ts
this.oneSignal.handleNotificationOpened().subscribe((data) => {
let payload = data; // getting id and action in additionalData.
this.redirectToPage(payload);
});
redirectToPage(data) {
let type
try {
type = data.notification.payload.additionalData.type;
} catch (e) {
console.warn(e);
}
switch (type) {
case 'Followers': {
this.navController.push(UserProfilePage, { userId: data.notification.payload.additionalData.uid });
break;
} case 'comment': {
this.navController.push(CommentsPage, { id: data.notification.payload.additionalData.pid })
break;
}
}
}
A better solution would be to reset the current nav stack and recreate it. Why?
Lets see this scenario:
TodosPage (rootPage) -> TodoPage (push) -> CommentsPage (push)
If you go directly to CommentsPage the "go back" button won't work as expected (its gone or redirect you to... who knows where :D).
So this is my proposal:
this.oneSignal.handleNotificationOpened().subscribe((data) => {
// Service to create new navigation stack
this.navigationService.createNav(data);
});
navigation.service.ts
import {Injectable} from '#angular/core';
import {App} from 'ionic-angular';
import {TodosPage} from '../pages/todos/todos';
import {TodoPage} from '../pages/todo/todo';
import {CommentsPage} from '../pages/comments/comments';
#Injectable()
export class NavigationService {
pagesToPush: Array<any>;
constructor(public app: App) {
}
// Function to create nav stack
createNav(data: any) {
this.pagesToPush = [];
// Customize for different push notifications
// Setting up navigation for new comments on TodoPage
if (data.notification.payload.additionalData.type === 'NEW_TODO_COMMENT') {
this.pagesToPush.push({
page: TodoPage,
params: {
todoId: data.notification.payload.additionalData.todoId
}
});
this.pagesToPush.push({
page: CommentsPage,
params: {
todoId: data.notification.payload.additionalData.todoId,
}
});
}
// We need to reset current stack
this.app.getRootNav().setRoot(TodosPage).then(() => {
// Inserts an array of components into the nav stack at the specified index
this.app.getRootNav().insertPages(this.app.getRootNav().length(), this.pagesToPush);
});
}
}
I hope it helps :)