I am having a hard time navigating to a screen when a background FCM message receives. Currently, I am sending an FCM message with some data and when it gets received by a device then it calls this package that shows a calling notification, and when the user clicks on accept call option then I want to open my app and navigate to the desired screen. I use GetX for navigation and when I try to go to another screen it gives this exception:
I have tried almost everything to work this out but I am still unable to solve this problem.
my FirebaseMessaging.onBackgroundMessage Background handler which receives the notification also listens to user feedback on whether the call is accepted or rejected:
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
var incoming = <String, dynamic>{
'id': message.data['callerID'],
'nameCaller': message.data['callerName'],
'appName': 'Callkit',
'avatar': message.data['callerImage'],
'handle': '',
'type': 0,
'duration': 30000,
'extra': <String, dynamic>{'userId': '1a2b3c4d'},
'headers': <String, dynamic>{'apiKey': 'Abc#123!', 'platform': 'flutter'},
'android': <String, dynamic>{
'isCustomNotification': true,
'isShowLogo': false,
'ringtonePath': 'ringtone_default',
'backgroundColor': '#0955fa',
//'backgroundUrl': 'https://i.pravatar.cc/500',
'actionColor': '#4CAF50'
}};
await FlutterCallkitIncoming.showCallkitIncoming(incoming);
try {
FlutterCallkitIncoming.onEvent.listen((event) {
switch (event!.name) {
case CallEvent.ACTION_CALL_INCOMING:
print('INCOMING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
break;
case CallEvent.ACTION_CALL_START:
// TODO: started an outgoing call
// TODO: show screen calling in Flutter
break;
case CallEvent.ACTION_CALL_ACCEPT:
print('accepted');
Get.offAll(()=> Incoming(
userName: null,
userImage: null,
userID: null,
userUsername: null));
break;
case CallEvent.ACTION_CALL_DECLINE:
print('rejected');
break;
case CallEvent.ACTION_CALL_ENDED:
// TODO: ended an incoming/outgoing call
break;
case CallEvent.ACTION_CALL_TIMEOUT:
// TODO: missed an incoming call
break;
case CallEvent.ACTION_CALL_CALLBACK:
// TODO: only Android - click action `Call back` from missed call notification
break;
case CallEvent.ACTION_CALL_TOGGLE_HOLD:
// TODO: only iOS
break;
case CallEvent.ACTION_CALL_TOGGLE_MUTE:
// TODO: only iOS
break;
case CallEvent.ACTION_CALL_TOGGLE_DMTF:
// TODO: only iOS
break;
case CallEvent.ACTION_CALL_TOGGLE_GROUP:
// TODO: only iOS
break;
case CallEvent.ACTION_CALL_TOGGLE_AUDIO_SESSION:
// TODO: only iOS
break;
}
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
});
} on Exception {}
}
Similar to the answer that Peter Koltai gave, your background handler is isolated from your application's context and so it's not possible to route to screens (which require a context) directly from your handler.
One possible solution is to implement an Android service using native code that communicates with Flutter via a MethodChannel and on the event that a call is accepted, you can navigate screens.
You can change your MaterialApp() with GetMaterialApp(), and check if you are using get.to(AnotherPage()), instead use get.to(()=>AnotherPage()).
i never tested it before but i think you should use wakelock to keep your screen awake
enter link description here
Related
Currently Im adding guest mode feature so The App that I'm developing allow user to access some feature of the app without login (guest mode).
When the user clicked profile on bottom navigation, the app will do Intent and finish(java) or pushNamedAndRemoveUntil but it crashed.
Exception has occurred.
FlutterError (setState() or markNeedsBuild() called during build.
This _ModalScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
_ModalScope-[LabeledGlobalKey<_ModalScopeState>#bfd49]
The widget which was currently being built when the offending call was made was:
MainPage)
Widget moveToLogin() {
Navigator.of(context)
.pushNamedAndRemoveUntil('/sign-in', (Route<dynamic> route) => false);
return Container(
child: Text("Logging in..."),
);
}
Widget body() {
switch (currentIndex) {
case 0:
return HomePage();
break;
case 1:
return ActivityPage();
break;
case 2:
return ShoppingPage();
break;
case 3:
return ChatPage();
break;
case 4:
if (user.nama_customer == "guest") {
return moveToLogin();
} else {
return ProfilePage();
}
break;
default:
return HomePage();
}
}
create parameter first on statefulWidget
i've solved it with guest session. and the problem here was because of using "widget". it should be void
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?
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 want to be able to listen (BroadcastReceiver?) for whenever a notification occurs for my app and for the calendar app, even if the user doesn't interact with it and even if the screen is off.
I also want to be able to know when a user swipes away the notification.
I know that AccessibilityEvent can enable doing the first thing (listening for notifications) but it can't do the second one (Cannot listen for notification dismissal). Is there another way?
How would I do this? Can I at least have my app/listener called when my own notifications occur (they're for calendar events) and know that the notification is still in the notification tray?
You can listen all the calendar notification info. How you say, you should use an AccessibilityService and switch the notification type. Then you get the package name an compare if it's from google calendar.
public void onAccessibilityEvent(AccessibilityEvent event) {
try {
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_VIEW_CLICKED:
Log.d(LOG_SERVICE, "Click");
break;
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
Log.d(LOG_SERVICE, "Focused");
break;
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
Log.d(LOG_SERVICE, "Ihas been received a not" + type);
String pack = (String) event.getPackageName();
if (pack.equalsIgnoreCase("com.google.android.calendar")) {
//The event information.
}
break;
default:
type = "otro";
Log.d(LOG_SERVICE, "otro");
break;
}
AccessibilityNodeInfo notinfo = event.getSource();
if (notinfo != null) {
doSomethingWithNodeInfo(notinfo);
}
} catch (Exception e) {
e.printStackTrace();
}
If the Android device is with ICS or more. You can use AccessibilityNodeInfo if the app developer design their notifications with accessibility.
I am currently using Beem's source code to do developments. I would like to implement the chat state notifications. I have look through the codes and found that there is a setState() method, but I believe it has not been implemented and I have no clues about how to do it. If I use Adium to type a message to the Beem user, the Beem user is able to see that the Adium user is composing a message. But if both users are using Beem, then it does not display if the user is composing a message. Therefore, I would like to try to implement the chat state notification. How do I go about doing it? Is there any guides out there? Can someone help me? Thanks!
Add this code to setState method in ChatAdapter.java file.
org.jivesoftware.smack.packet.Message message = new org.jivesoftware.smack.packet.Message();
ChatStateExtension extension = null;
switch (state) {
case "composing":
extension = new ChatStateExtension(ChatState.composing);
break;
case "active":
extension = new ChatStateExtension(ChatState.active);
break;
case "inactive":
extension = new ChatStateExtension(ChatState.inactive);
break;
case "gone":
extension = new ChatStateExtension(ChatState.gone);
break;
case "paused":
extension = new ChatStateExtension(ChatState.paused);
break;
}
message.addExtension(extension);
try {
mAdaptee.sendMessage(message);
} catch (XMPPException e) {
e.printStackTrace();
}