I tried to implement and firebase listener in my Xamarin iOS App.
But, if my app is in foreground and an firebase cloud message receives, CrossPushNotification.Current.OnNotificationReceived is not be called.
What's problem?
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
global::Xamarin.Forms.Forms.Init();
App.Configure();
this.RegisterForRemoteNotifications(launchOptions);
this.LoadApplication(new MyApp());
return base.FinishedLaunching(uiApplication, launchOptions);
}
private void RegisterForRemoteNotifications(NSDictionary launchOptions)
{
PushNotificationManager.Initialize(launchOptions, true);
CrossPushNotification.Current.RegisterForPushNotifications();
Messaging.SharedInstance.ShouldEstablishDirectChannel = true;
CrossPushNotification.Current.OnTokenRefresh += (s, p) =>
{
System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}");
Messaging.SharedInstance.ApnsToken = p.Token;
};
CrossPushNotification.Current.OnNotificationReceived += (s, p) =>
{
System.Diagnostics.Debug.WriteLine("Received");
};
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
PushNotificationManager.DidRegisterRemoteNotifications(deviceToken);
Messaging.SharedInstance.ApnsToken = deviceToken;
}
// To receive notifications in background in any iOS version
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
PushNotificationManager.DidReceiveMessage(userInfo);
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Messaging.SharedInstance.ShouldEstablishDirectChannel = false;
}
}
If you use the Firebase.You should use the package Plugin.FirebasePushNotification from NuGetnot Plugin.PushNotification.
Change the method like following
CrossFirebasePushNotification.Current.OnTokenRefresh += (s,p) =>
{
System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}");
};
CrossFirebasePushNotification.Current.OnNotificationReceived += (s,p) =>
{
System.Diagnostics.Debug.WriteLine("Received");
};
For more detail you can refer here
Related
We customize the toolbar menu for navigation back button in xamarin forms ios. I am getting below crash in appcenter.
ToolbarMenuCustomRenderer.ViewWillAppear (System.Boolean animated)
SIGABRT: Object reference not set to an instance of an object
Code snippet below:
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
CustomToolbarContentPage page = Element as CustomToolbarContentPage;
if (page == null)
return;
#region for soft back button
UIViewController root = NavigationController.TopViewController;
if (!page.NeedOverrideSoftBackButton)
return;
string title = "<" + (string.IsNullOrEmpty(NavigationPage.GetBackButtonTitle(Element)) ? "" : NavigationPage.GetBackButtonTitle(Element));
root.NavigationItem.SetLeftBarButtonItem(
new UIBarButtonItem(title, UIBarButtonItemStyle.Plain, (sender, args) =>
{
page.OnSoftBackButtonPressed();
}), true);
#endregion
}
How to resolve this in xamarin forms ios?
I create a sample app with your code and works fine, you can check the code below to see if you miss something:
NewPage:
namespace My_Forms_test.Views
{
public partial class NewPage2 : ContentPage
{
public NewPage2()
{
InitializeComponent();
}
public void OnSoftBackButtonPressed()
{
Navigation.PopToRootAsync();
}
}
}
CustomRenderer:
[assembly:ExportRenderer(typeof(NewPage2),typeof(MyPageRenderer))]
namespace My_Forms_test.iOS
{
public class MyPageRenderer:PageRenderer
{
public MyPageRenderer()
{
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
NewPage2 page=Element as NewPage2;
UIViewController root = NavigationController.TopViewController;
string title = "<" + (string.IsNullOrEmpty(NavigationPage.GetBackButtonTitle(Element)) ? " " : NavigationPage.GetBackButtonTitle(Element));
root.NavigationItem.SetLeftBarButtonItem(
new UIBarButtonItem(title, UIBarButtonItemStyle.Plain, (sender, args) =>
{
page.OnSoftBackButtonPressed();
Console.WriteLine("This method is trigged"); }), true);
}
}
Here are ScreenShots:
You can also refer to this documents https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/contentpage
In my xamarin.forms app, I am trying to create background task and service in ios and android . I created service for android and task for ios. According to the service and task I am trying to call a web API in my shared code. I am communicating to my shared code using Messaging center. In android the communication works fine. But in ios the messaging center not subscribing in the shared code.
My ios background Task
public class iOSLongRunningTaskExample
{
nint _taskId;
CancellationTokenSource _cts;
public async Task Start()
{
_cts = new CancellationTokenSource();
_taskId = UIApplication.SharedApplication.BeginBackgroundTask("LongRunningTask", OnExpiration);
try
{
// Here I am calling the shared code
Device.BeginInvokeOnMainThread(() =>
MessagingCenter.Send<Object>(new Object(),
"CheckNotificationAPI")
);
}
catch (OperationCanceledException)
{
}
finally
{
//if (_cts.IsCancellationRequested)
//{
// var message = new CancelledMessage();
// Device.BeginInvokeOnMainThread(
// () => MessagingCenter.Send(message, "CancelledMessage")
// );
//}
}
UIApplication.SharedApplication.EndBackgroundTask(_taskId);
}
public void Stop()
{
_cts.Cancel();
}
void OnExpiration()
{
_cts.Cancel();
}
}
My Shared code
MessagingCenter.Subscribe<Object>(this, "CheckNotificationAPI", async (sender) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("Message", "Recieved", "OK");
});
});
My android service which works fine
[Service]
public class LongRunningTaskService : Service
{
CancellationTokenSource _cts;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
_cts = new CancellationTokenSource();
Task.Run(() => {
try
{
//INVOKE THE SHARED CODE
Device.BeginInvokeOnMainThread(()=>
MessagingCenter.Send<Object>(new Object(), "CheckNotificationAPI")
);
}
catch (System.OperationCanceledException)
{
}
finally
{
//if (_cts.IsCancellationRequested)
//{
// var message = new CancelledMessage();
// Device.BeginInvokeOnMainThread(
// () => MessagingCenter.Send(message, "CancelledMessage")
// );
//}
}
}, _cts.Token);
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
if (_cts != null)
{
_cts.Token.ThrowIfCancellationRequested();
_cts.Cancel();
}
base.OnDestroy();
}
}
Any help is appreciated.
I start the foreground service, when my app is going to background. On that time I go to setting page and change the permission status to Deny on Camera. My app get crashed on android 8.0.
My foreground service code like below:
namespace MyProj.Droid.Services
{
[Service]
public class MyProjService : Service
{
CancellationTokenSource _cts;
private static ILogger logger = DependencyService.Get<ILogManager>().GetLog();
public const string LOCATION_CHANNEL = "default";
NotificationManager manager;
NotificationCompat.Builder notification;
public override void OnCreate()
{
base.OnCreate();
manager = (NotificationManager)Forms.Context.GetSystemService("notification");
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
StartLocationServiceForeground();
return StartCommandResult.Sticky;
}
void StartLocationServiceForeground()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var chan1 = new NotificationChannel(LOCATION_CHANNEL,
new Java.Lang.String("Primary"), NotificationImportance.High);
manager.CreateNotificationChannel(chan1);
notification = new NotificationCompat.Builder(Forms.Context, LOCATION_CHANNEL);
notification.SetOngoing(true)
.SetSmallIcon(Resource.Drawable.icon_transparent)
.SetContentTitle("MyProj 24x7 Trucker is running background")
.SetContentText("Tab for more information or to stop the app")
.SetColor(0x9c6114)
.SetPriority(NotificationCompat.PriorityHigh);
StartForeground(1, notification.Build());
}
}
catch(System.Exception ex)
{
}
}
public override void OnDestroy()
{
StopForeground(true);
if (manager!=null)
{
manager.CancelAll();
}
base.OnDestroy();
}
}
}
Can anyone please help to resolve this issue.
I'm using Branch.io in a Xamarin Forms app, and my goal is to send push notifications through Azure Notification Hubs with Branch links in them to open the app and do something. I seem to have it all configured perfectly fine for iOS, but in Android I'm receiving the push notification and the correct activity is coming up, but InitSessionComplete is not called on my IBranchBUOSessionInterface object.
I'm suspecting the issue is in the way I'm creating the PendingIntent, but I could be totally wrong. Again, InitSessionComplete is called in every other circumstance except for when I receive a push notification with a Branch link.
All relevant code is below. Thanks!
MainApplication.cs
using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using BranchXamarinSDK;
using Plugin.CurrentActivity;
namespace MyCompany.MyApp.Droid
{
//You can specify additional application information in this attribute
[Application]
[MetaData("io.branch.sdk.auto_link_disable", Value = "false")]
[MetaData("io.branch.sdk.TestMode", Value = "true")]
[MetaData("io.branch.sdk.BranchKey", Value = "#string/branch_key")]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication (IntPtr handle, JniHandleOwnership transer)
: base (handle, transer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
RegisterActivityLifecycleCallbacks (this);
BranchAndroid.GetAutoInstance(ApplicationContext);
}
public override void OnTerminate ()
{
base.OnTerminate ();
UnregisterActivityLifecycleCallbacks (this);
}
public void OnActivityCreated (Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed (Activity activity)
{
}
public void OnActivityPaused (Activity activity)
{
}
public void OnActivityResumed (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState (Activity activity, Bundle outState)
{
}
public void OnActivityStarted (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped (Activity activity)
{
}
}
}
MainActivity.cs
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Util;
using BranchXamarinSDK;
using MyCompany.Shared.Droid.Modules;
using App = MyCompany.MyApp.Core.App;
namespace MyCompany.MyApp.Droid
{
[Activity (Theme = "#style/Custom.Holo",
Label = "MyApp",
Icon = "#drawable/icon",
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait,
LaunchMode = LaunchMode.SingleTask,
MainLauncher = true)]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "myapp",
DataHost = "open")]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "https",
DataHost = "mycompanymyapp.test-app.link")]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsApplicationActivity, IBranchBUOSessionInterface
{
public const string Tag = "MainActivity";
private App _app;
internal static readonly string ChannelId = "MyCompany";
internal static readonly int NotificationId = 100;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
if (key == null) { continue; }
var value = Intent.Extras.GetString(key);
Log.Debug(Tag, "Key: {0} Value: {1}", key, value);
}
}
// Removes icon from android navbar
ActionBar.SetIcon(Android.Resource.Color.Transparent);
Xamarin.Forms.Forms.Init (this, savedInstanceState);
BranchAndroid.Debug = true;
var androidModule = new MyCompanyAndroidServicesModule ();
var app = new App (androidModule);
BranchAndroid.Init(this, GetString(Resource.String.branch_key), this);
LoadApplication(app);
_app = app;
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
_app.InitSessionComplete(buo, blp);
}
public void SessionRequestError(BranchError error)
{
_app.SessionRequestError(error);
}
#endregion
protected override void OnNewIntent(Intent intent)
{
Intent = intent;
}
}
}
MyCompany.MyApp.Core.cs
#region Libraries
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Autofac;
using BranchXamarinSDK;
using MyCompany.Core;
using MyCompany.Core.Factories;
using MyCompany.Core.Helpers;
using MyCompany.Core.Services;
using MyCompany.Core.ViewModels;
using MyCompany.MyApp.Core.ViewModels;
using Xamarin.Forms;
using Device = Xamarin.Forms.Device;
#endregion Libraries
namespace MyCompany.MyApp.Core
{
public class App : Application, ILoginApp, IBranchBUOSessionInterface
{
#region Variables
readonly IViewFactory _viewFactory;
readonly INavigationService _navService;
#endregion Variables
#region Constructor
public App (Module platformServiceModule = null)
{
var bootstrapper = new TimeAppBootstrapper ();
if (platformServiceModule != null)
bootstrapper.AddModule (platformServiceModule);
bootstrapper.Run ();
_viewFactory = bootstrapper.Container.Resolve<IViewFactory> ();
_navService = bootstrapper.Container.Resolve<INavigationService> ();
Page startView = new NavigationPage (_viewFactory.Resolve<LoginV2ViewModel> ()) {
BarBackgroundColor = Color.White,
BarTextColor = Color.FromRgb(34, 149, 236)
};
MainPage = startView;
}
#endregion Constructor
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
/// <summary>
/// Application developers override this method to perform actions
/// when the application resumes from a sleeping state.
/// </summary>
protected override void OnResume ()
{
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
if (blp.feature == "feature")
{
_navService.NavigateTo<FeatureViewModel>();
}
}
public void SessionRequestError(BranchError error)
{
var e = error;
}
#endregion
}
}
FirebaseMessagingService.cs
using Android.App;
using Android.Content;
using Android.Util;
using Firebase.Messaging;
using Xamarin.Essentials;
namespace Crowdkeep.Time.Droid.Services
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
const string TAG = "MyFirebaseMessagingService";
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "From: " + message.From);
var notificationManager = NotificationManager.FromContext(this);
InitializeChannels(notificationManager);
if (IsProductionNotification(message))
{
SendProductionNotification(message, notificationManager, this);
}
else
{
SendTestNotification(message, notificationManager);
}
}
private static bool IsProductionNotification(RemoteMessage message)
{
return message.GetNotification() != null;
}
private void SendTestNotification(RemoteMessage message, NotificationManager notificationManager)
{
var notification = CreateNotification(message.Data["message"], message.Data["branch"], this);
notificationManager.Notify(0, notification);
}
private static void SendProductionNotification(RemoteMessage message, NotificationManager manager, Context context)
{
Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
var notification = CreateNotification(message.GetNotification().Body, "https://mycompanymyapp.test-app.link/feature", context);
manager.Notify(0, notification);
}
private static void InitializeChannels(NotificationManager manager)
{
if (DeviceInfo.Version.Major < 8 || manager.GetNotificationChannel(MainActivity.ChannelId) != null)
{
return;
}
var channel = new NotificationChannel(MainActivity.ChannelId, "Crowdkeep", NotificationImportance.Default)
{
Description = "Default Channel"
};
manager.CreateNotificationChannel(channel);
}
private static Notification CreateNotification(string messageBody, string link, Context context)
{
var pendingIntent = SetupNotificationIntent(link, context);
var notificationBuilder = new Notification.Builder(context, MainActivity.ChannelId)
.SetContentTitle("Message")
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(messageBody)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetVisibility(NotificationVisibility.Public);
return notificationBuilder.Build();
}
private static PendingIntent SetupNotificationIntent(string link, Context context)
{
var intent = new Intent(context, typeof(MainActivity));
intent.SetFlags(ActivityFlags.ClearTop);
intent.PutExtra("branch", link);
intent.PutExtra("branch_force_new_session", true);
var pendingIntent = PendingIntent.GetActivity(context, MainActivity.NotificationId, intent, PendingIntentFlags.OneShot);
return pendingIntent;
}
}
}
Json Sent via Notification:
{"data":{"message":"Click this notification to go to the best feature of the app!", "branch": "https://mycompanymyapp.test-app.link/feature"}}
EDIT (1/7/2019)
I'm able to reproduce this in the Test Bed. I forked my own copy of Branch and added a branch called initsessioncomplete-push-notification-issue, which you can access here. I followed these instructions to setup Firebase and Azure Notification Hubs. In the solution, the only thing you'll have to modify are the two strings in the AppConstants class in the TestBed.Droid project, and add your google-services.json file to the root of the Droid project (the .csproj is already configured properly to read it).
As far as I can tell, I see a successful call being made to the Branch API when I click the push notification, but InitSessionComplete is never called.
Joon from Branch here. Can you double check that you are using the correct Branch Key in your AndroidManifest? I noticed that you are testing with a test Branch Link so you'll have to use your test Branch Key.
Can't start intent service
I am writing code for synch data for every 10 minute whenapp is in forground,background or sleep . I had write weakfulintentservice for it calling it from Alaramreceiver but AppService doesn't get call.
AppService
public class AppService : WakefulIntentService
{
public AppService() : base("AppService")
{
}
protected override void DoWakefulWork(Intent intent)
{
Toast.MakeText(this, "In service", ToastLength.Short).Show();
Log.Info("AppService", "I'm awake! I'm awake!");
}
}
WeakFulIntentService
abstract public class WakefulIntentService : IntentService
{
abstract protected void DoWakefulWork(Intent intent);
public static string NAME = "com.jondouglas.wakeful.WakefulIntentService";
public static string LAST_ALARM = "lastAlarm";
private static volatile PowerManager.WakeLock lockStatic = null;
[MethodImpl(MethodImplOptions.Synchronized)]
private static PowerManager.WakeLock GetLock(Context context)
{
if (lockStatic == null)
{
PowerManager manager = (PowerManager) context.GetSystemService(Context.PowerService);
lockStatic = manager.NewWakeLock(WakeLockFlags.Partial, NAME);
lockStatic.SetReferenceCounted(true);
}
return (lockStatic);
}
public static void SendWakefulWork(Context context, Intent intent)
{
GetLock(context.ApplicationContext); //Possibly use of acquire here
context.StartService(intent);
}
public static void SendWakefulWork(Context context, Type classService)
{
SendWakefulWork(context, new Intent(context, classService));
}
public static void ScheduleAlarms(IAlarmListener alarmListener, Context context)
{
ScheduleAlarms(alarmListener, context, true);
}
public static void ScheduleAlarms(IAlarmListener alarmListener, Context context, bool force)
{
ISharedPreferences preferences = context.GetSharedPreferences(NAME, 0);
long lastAlarm = preferences.GetLong(LAST_ALARM, 0);
if (lastAlarm == 0 || force ||
(DateTime.Now.Millisecond > lastAlarm &&
DateTime.Now.Millisecond - lastAlarm > alarmListener.GetMaxAge()))
{
AlarmManager manager = (AlarmManager) context.GetSystemService(Context.AlarmService);
Intent intent = new Intent(context, typeof(AlarmReceiver));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(context, 0, intent, 0);
alarmListener.ScheduleAlarms(manager, pendingIntent, context);
}
}
public static void CancelAlarms(Context context)
{
AlarmManager manager = (AlarmManager) context.GetSystemService(Context.AlarmService);
Intent intent = new Intent(context, typeof (AlarmReceiver));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(context, 0, intent, 0);
manager.Cancel(pendingIntent);
context.GetSharedPreferences(NAME, 0).Edit().Remove(LAST_ALARM).Commit();
}
public WakefulIntentService(string name) : base(name)
{
SetIntentRedelivery(true);
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
PowerManager.WakeLock wakeLock = GetLock(this.ApplicationContext);
if (!lockStatic.IsHeld || (flags & StartCommandFlags.Redelivery) != 0)
{
wakeLock.Acquire();
}
return base.OnStartCommand(intent, flags, startId);
return (StartCommandResult.RedeliverIntent);
}
protected override void OnHandleIntent(Intent intent)
{
try
{
DoWakefulWork(intent);
}
finally
{
PowerManager.WakeLock wakeLock = GetLock(this.ApplicationContext);
if (wakeLock.IsHeld)
{
try
{
wakeLock.Release();
}
catch (Exception ex)
{
Log.Error(Class.SimpleName, "Exception when releasing wakelock", ex);
//Log exception when releasing wakelock
}
}
}
}
public interface IAlarmListener
{
void ScheduleAlarms(AlarmManager manager, PendingIntent pendingIntent, Context context);
void SendWakefulWork(Context context);
long GetMaxAge();
}
CallToAppService
public void SendWakefulWork(Context context)
{
WakefulIntentService.SendWakefulWork(context, typeof(AppService));
}
The call for AppService context.StartService(intent); from weakfulintentservice execute perfectly
but AppService cant start In xamarin.android.
Kindly help me to solve this issue.
The call for AppService context.StartService(intent); from weakfulintentservice execute perfectly but AppService cant start In xamarin.android.
You can refer to Started Services, in your Xamarin.Android code where you want to start your AppService, you can code as simply as this:
StartService (new Intent (this, typeof(AppService)));
If you want to start a service with an intent filter, you can refer to this part.
Also you may refer to the case I answered several days ago: Xamarin Android : Change UI TextView text from Service or Receiver.