Hi
While writing this, I got it working, but posting anyway in case it helps someone else
I am having issues closing a page on Xamarin.Forms running on Android. My app is monolithic - the Android activity launches a Xamarin.forms page based on the content of an Android intent. For this reason I have the decoration:
LaunchMode = LaunchMode.SingleTask, ExcludeFromRecents = true, NoHistory = true
On my Android activity - When back or home is pressed, there is nothing in resents. If the activity is already active and another intent is bound for my app, no new activity is launched (Launchmode.SingleTask), rather I simply switch the active page (while maintaining no history).
This intent comes via a tap, bluetooth characteristic change or via NFC. One of my pages is simply a popup on an NFC intent that initiates a connection to a BLE device.
Onto the Issue
In one of my pages I have a simple 'Close' button, the page is launched via an intent as above, currently (for debug) the close is wired to the codebehind (it will end up being done from the viewmodel off a timer). I have thus far tried three methods to close the page all of which result in a crash:
1) Close();
2) await Navigation.PopModalAsync();
3) Through the DependancyService using an interface that calls: activity.FinishAffinity(); (also tried just Finish() and FinishActivity()
For 1) and 2), I get exceptions thus
1) exception = {System.InvalidCastException: Specified cast is not valid. at ProteusPatientApp.Views.ConnectionStatusPage.Close () [0x00001] in ConnectionStatusPage.xaml.cs:32
2) Message = "Index was out of range. Must be non-negative and less than the size of the collection.\nParameter nam…"
I believe that both of the above failed due to the monolithic, no history, no navigation stack nature of my app.
For three, one needed to get the activity, but the getter mechanism 'Xamarin.Forms.Forms.Context' is obsoleted. I tried 'Android.App.Application.Context' but this resulted in an 'InvalidCastException'.
The solution was to implement the IActivityLifecycleCallbacks interface. There are two ways of doing this - using James Montemagno's Plugin.CurrentActivity which implements the IActivityLifecycleCallbacks in a file MainApplication.cs, or to manually implement the interface. Since implementing the interface is easy, I went for the manual method. The hints for this came from: https://stackoverflow.com/questions/47353986/xamarin-forms-forms-context-is-obsolete and you can simply paste the code from this into a new file MainApplication.cs.
I moved the decoration for [Application] from the Manifest into the MainApplication.cs thus:
[Application(Label="ProteusPatientApp.Android",Icon="@drawable/spacelabs_logo", Theme="@android:style/Theme.Translucent.NoTitleBar")]
public partial class MainApplication : Application, Application.IActivityLifecycleCallbacks
........
I also had to remove the code:
[assembly: Application(Debuggable = true)]
From my application as otherwise the thing threw a load of Java errors during compilation - I never for to the bottom of this. Anyway, I can now Close the current Activity (or a Popup window implemented using Rg.Plugins.Popup either with button press or programatically.
Following on from that you just need an interface in your UI code 'ICloseApplication.cs':
namespace ProteusPatientApp.Interfaces
{
public interface ICloseApplication
{
void CloseApplication();
}
}
And an implementation for it in your Android code 'CloseApplication.cs':
[assembly: Xamarin.Forms.Dependency(typeof(ApplicationCloser))]
namespace ProteusPatientApp.Droid
{
public class ApplicationCloser : ICloseApplication
{
public void CloseApplication()
{
var activity = (Activity)MainApplication.ActivityContext;
activity.FinishAffinity();
}
}
}
On Android my service does the work, and calling activity.FinnishAffinity() only closes the activities - the service sits there waiting to do work. Things are different on iOS as it has no concept of services or (for non Voip apps), boot start apps. If you do want to close the app in iOS, you can implement the close interface thus. NB: Apparently this is not recommended - iOS likes to maintain the lifecycle of apps by its-self, and going DIY can cause you issues with store approvals - so says the internet. I have not done the iOS bit for my app as yet.
[assembly: Xamarin.Forms.Dependency(typeof(ApplicationCloser))]
namespace ProteusPatientApp.iOS
{
public class ApplicationCloser : ICloseApplication
{
public void CloseApplication()
{
Thread.CurrentThread.Abort();
}
}
}
Nigel