Xamarin.Forms
Xamarin.Forms copied to clipboard
[Bug] Xamarin android - push notification Intent extras lost
Description
I have follow this guide to setup push notification in my Xamarin Forms app.
I have an app with two activity, one splash activity
Activity(Theme = "@style/SplashTheme",
MainLauncher = true, ScreenOrientation = ScreenOrientation.Portrait, NoHistory = true)]
public class SplashAct: Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
StartActivity(typeof(MainActivity));
Finish();
}
}
and the MainActivity
[Activity(
ClearTaskOnLaunch = false,
FinishOnTaskLaunch = false,
Theme = "@style/MainTheme",
LaunchMode = LaunchMode.SingleTask,
ScreenOrientation = ScreenOrientation.Portrait,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public partial class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
in the main activity I have override the OnNewIntent method
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
ProcessNotificationActions(intent);
}
and I have tried to send a notification using a web api published on Azure with this body:
{
"text": "Test msg",
"action": "action_a"
}
on device the text is correctly shown, but when the code reach the OnNewIntent method the intent has not extras so I lost the "action" value.
NB. I can't use LaunchMode = LaunchMode.SingleTop otherwise the OnNewIntent is not called, the OnCreate is called and the app shows a blank screen.
This happens only if the application is in background or closed. If it is in foreground all works fine.
Expected Behavior
I can get the extra intent.
Actual Behavior
the extra intent is lost.
Reproduction Link
Attached a sample, a video and info to make the POSTMAN\RESTER request.
Having the same issue. Have you found any work arounds?
Having the same issue. Have you found any work arounds?
Hi @Tronald, I have solve by using this code as OnCreate of the SplashActivity
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var intent = new Intent(Application.Context, typeof(MainActivity));
intent.PutExtras(Intent);
StartActivity(intent);
Finish();
}
@Raystorm7 thanks! Where are you pulling Intent from in your intent.PutExtras() call?
As the below code is in the MainActivity, I am unsure as to how to gather push notification intent from SplahActivity.
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
ProcessNotificationActions(intent);
}
To make things more fun, Android 28 changes splash screen behavior so we will have to figure this all out again :(
I appreciate the response!
@Tronald in MainActivity I've done this:
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
ProcessNotificationActions(intent);
}
void ProcessNotificationActions(Intent intent)
{
try
{
if (intent.Extras != null)
{
Bundle bundle = intent.Extras;
Dictionary<string, object> dict = bundle.KeySet()
.ToDictionary<string, string, object>(key => key, key => bundle.Get(key));
}
if (intent?.HasExtra("action") == true)
{
var action = intent.GetStringExtra("action");
if (!string.IsNullOrEmpty(action))
{
OnNewIntentNotifica = true;
NotificationActionService.TriggerAction(action);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
the "action" is a part of my push arguments that is like this:
{
"text": "Test message",
"action": "@@goToChat@@1b872cde-8a84-46d0-b3a3-153281fe818e@@",
"tags": ["username"]
}
the NotificationActionService.TriggerAction(action) method contains the login to manage the notification, in my case I open a View with a specific chat :)
@Raystorm7 thank you so much, you are a wizard!
I finally got it working after some trial and error. I had realized that the Microsoft provided guide has the SplashActivity inherit from AppCompatActivity instead of Activity like in your example. The guide implements code almost identical to yours so I missed it, but once I made the swap and moved my logic from OnResume to OnCreate it began to work flawlessy.
Microsoft's documentation for Xamarin Forms seems full of incorrect code I have been noticing. It has made this journey a bit tough at times lol.
Thanks for your help!
Everything works as a charm, as long as the Notification field is removed. This is not a Xamarin bug, but an Android framework change.
Well, maybe it's late for many, but this is my Firebase Backend Code (using official Firebase.Admin.Sdk):
public void Send(BaseNotification baseNotification)
{
var data = baseNotification.ToDictionary();
ValidatePayload(data);
var msg = new Message
{
Token = baseNotification.To,
Android = new AndroidConfig()
{
Priority = Priority.High
},
Data = data
};
string result = FirebaseMessaging.GetMessaging(app).SendAsync(msg).Result;
if (result == null || result.Length == 0)
{
throw new InvalidOperationException("Failed to deliver message.");
}
}
Everything works as a charm, as long as the Notification field is removed. This way, your background execution handler is invoked. Additionally, this is my relevant Xamarin code for Activity:
[Activity(
Label = "XyZ_Not_That_Relevant",
Icon = "@mipmap/ic_launcher",
Theme = "@style/MainTheme",
MainLauncher = true,
Exported = true,
ResizeableActivity = false,
LaunchMode = LaunchMode.SingleTop,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
[IntentFilter(
actions: new string[] { Intent.ActionView },
Categories = new string[] { Intent.CategoryDefault },
DataScheme = NotificationConstants.FieldScheme,
DataHost = NotificationConstants.FieldHost
)]
From the android firebase notification service, I have the following snippet:
void SendNotification(RemoteMessage message)
{
var payload = NotificationMarshaller.BuildNotificationFromKeyValuePair(message.Data);
bool isAppInForeground = CheckAppIsInForeground();
if (isAppInForeground)
{
//Omitted for brevity, app is in foreground :)
return;
}
var dotNetUri = NotificationMarshaller.ConvertToUri(payload);
Android.Net.Uri javaAndroidUri = Android.Net.Uri.Parse(dotNetUri.AbsoluteUri);
Intent intent = new Intent(BaseContext,typeof(MainActivity));
intent.SetAction(Intent.ActionView);
intent.SetData(javaAndroidUri);
intent.SetFlags(ActivityFlags.FromBackground);
PendingIntentFlags flags = PendingIntentFlags.UpdateCurrent;
if (Build.VERSION.SdkInt >= BuildVersionCodes.S)
{
flags |= PendingIntentFlags.Immutable;
}
var rand = new Random().Next(2000);
var pendingIntent = PendingIntent.GetActivity(this, rand, intent, flags);
var channel = new NotificationChannelCompat.Builder(ChannelId, NotificationManagerCompat.ImportanceHigh)
.SetDescription(ChannelDescription)
.SetName(ChannelName)
.Build();
var notification = new NotificationCompat.Builder(this, channel.Id)
.SetSmallIcon(Resource.Drawable.ic_notification_private_collaboration)
.SetColor(Android.Graphics.Color.White.ToArgb())
.SetContentTitle(payload.DisplayTitle)
.SetContentText(payload.DisplayBody)
.SetContentIntent(pendingIntent)
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
.SetChannelId(channel.Id)
.SetAutoCancel(true)
.Build();
var manager = NotificationManagerCompat.From(this);
manager.CreateNotificationChannel(channel);
manager.Notify(rand, notification);
}