Xamarin.Forms handle notification launch (UWP) - c#

Xamarin documentation is a mess all over the internet but I cannot seem to find a solid example of how to handle my Xamarin.Forms application being launched from a notification or handling a notification being clicked while my application is already running.
So I am inside of the Xamarin.Forms.UWP application in the App.xaml.cs file that handles all the UWP launching. Within the OnActivated method I can see that my application receives a fire and gets the proper data from my toast notification. My question is how do I pass that data back into the Xamarin.Forms instance.
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
var _args = (args as LaunchActivatedEventArgs).Arguments;
}
Custom UWP Notification Service:
ToastContent content = new ToastContent()
{
Launch = "...",
Visual = new ToastVisual()
{
BindingGeneric = new ToastBindingGeneric()
{
Children =
{
new AdaptiveText()
{
Text = title,
HintMaxLines = 1
},
new AdaptiveText()
{
Text = body
}
}
}
},
Actions = new ToastActionsCustom()
{
Buttons =
{
new ToastButton(acceptAction.Key, $"action={acceptAction.Value}")
{
ActivationType = ToastActivationType.Foreground
},
new ToastButton(declineAction.Key, $"action={declineAction.Value}")
{
ActivationType = ToastActivationType.Background
}
}
}
};
var toast = new ToastNotification(content.GetXml());
ToastNotificationManager.CreateToastNotifier().Show(toast);
How do I go about sending this _args variable back into the running instance of my cross-platform application so that I can process the logic of navigating to a route, etc.
Furthermore if I have to write some custom code to handle this I will want the code to be easy to integrate the same functionally with the Xamarin.Forms.Android app in this project as well.

Related

How to load a specific dialog and it's resources without loading every dialog in the sample code?

Github link for the sample code I'm using
In the AdaptiveBot.cs file,
It creates a list of all the prompts available and takes user input and runs the specified prompt.
I want to modify it such that it loads only one dialog(There are 7 prompts in the sample folder and it gives a choice to load any one )
How would I go about to load only one dialog, for example just the MultiTurnPromptBot is needed to be loaded and the rest are not needed.
private void LoadDialogs()
{
System.Diagnostics.Trace.TraceInformation("Loading resources...");
//For this sample we enumerate all of the .main.dialog files and build a ChoiceInput as our rootidialog.
this.dialogManager = new DialogManager(CreateChoiceInputForAllMainDialogs());
this.dialogManager.UseResourceExplorer(this.resourceExplorer);
this.dialogManager.UseLanguageGeneration();
System.Diagnostics.Trace.TraceInformation("Done loading resources.");
}
private AdaptiveDialog CreateChoiceInputForAllMainDialogs()
{
var dialogChoices = new List<Choice>();
var dialogCases = new List<Case>();
foreach (var resource in this.resourceExplorer.GetResources(".dialog").Where(r => r.Id.EndsWith(".main.dialog")))
{
var name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(resource.Id));
dialogChoices.Add(new Choice(name));
var subDialog = resourceExplorer.LoadType<AdaptiveDialog>(resource);
dialogCases.Add(new Case($"{name}", new List<Dialog>() { subDialog }));
}
var dialog = new AdaptiveDialog()
{
AutoEndDialog = false,
Triggers = new List<OnCondition>() {
new OnBeginDialog() {
Actions = new List<Dialog>() {
new ChoiceInput() {
Prompt = new ActivityTemplate("What declarative sample do you want to run?"),
Property = "conversation.dialogChoice",
AlwaysPrompt = true,
Style = ListStyle.List,
Choices = new ChoiceSet(dialogChoices)
},
new SendActivity("# Running ${conversation.dialogChoice}.main.dialog"),
new SwitchCondition(){
Condition = "conversation.dialogChoice",
Cases = dialogCases
},
new RepeatDialog()
}
}
}
};
return dialog;
}
You can see that LoadDialogs is instantiating a dialog manager by passing an adaptive dialog into its constructor. So instead of creating the root dialog that starts all the other dialogs, you can just pass in one of those dialogs as the root dialog since they're all adaptive dialogs anyway. You can see that the declarative dialog files are loaded like this:
this.resourceExplorer.GetResources(".dialog")
And then the adaptive dialog instances are created out of them like this:
var subDialog = resourceExplorer.LoadType<AdaptiveDialog>(resource);
So you can do something like this:
private void LoadDialogs()
{
System.Diagnostics.Trace.TraceInformation("Loading resources...");
//For this sample we enumerate all of the .main.dialog files and build a ChoiceInput as our rootidialog.
//this.dialogManager = new DialogManager(CreateChoiceInputForAllMainDialogs());
this.dialogManager = new DialogManager(this.resourceExplorer.LoadType<AdaptiveDialog>(this.resourceExplorer.GetResource("MultiTurnPrompt.main.dialog")));
this.dialogManager.UseResourceExplorer(this.resourceExplorer);
this.dialogManager.UseLanguageGeneration();
System.Diagnostics.Trace.TraceInformation("Done loading resources.");
}
TL;DR: Replace this line:
this.dialogManager = new DialogManager(CreateChoiceInputForAllMainDialogs());
With this line:
this.dialogManager = new DialogManager(this.resourceExplorer.LoadType<AdaptiveDialog>(this.resourceExplorer.GetResource("MultiTurnPrompt.main.dialog")));

Keychain Prompt - Save To Keychain Xamarin

I'm trying to implement Keychain from start to finish for my Xamarin Application. At the moment I have the initial bit that will display the native prompt `Touch ID to Use Password'. That works successfully using the bit of code below.
public class TouchId : ITouchID
{
public Task<bool> AuthenticateUserIDWithTouchID()
{
var taskSource = new TaskCompletionSource<bool>();
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
taskSource.SetResult(success);
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return taskSource.Task;
}
}
My question is how do I display the native prompt asking if I would like to save to Keychain.
I've created a basic one using xamarin's Display Alert, but it doesn't quite look as nice as the native.

Unable to open App Store from app programmatically

I had been trying to open/navigate App Store from my application when there is a version upgrade.
For that I have written customrender which works perfectly fine for android. But it's not working for iOS. Following is the code written for iOS customrenderer. I attached the screenshot of the code in an attachment.
public class OpenAppStore : UIViewController, ISKStoreProductViewControllerDelegate, IOpenStore
{
public void OpenStore()
{
bool isSimulator = Runtime.Arch == Arch.SIMULATOR;
if (!isSimulator)
{
var storeViewController = new SKStoreProductViewController();
storeViewController.Delegate = this;
var id = SKStoreProductParameterKey.ITunesItemIdentifier;
var productDictionaryKeys = new NSDictionary("SKStoreProductParameterITunesItemIdentifier", 1389696261);
var parameters = new StoreProductParameters(productDictionaryKeys);
storeViewController.LoadProduct(parameters, (bool loaded, NSError error) =>
{
if ((error == null) && loaded)
{
this.PresentViewController(storeViewController, true, () =>
{
Console.WriteLine("SKStoreProductViewController Completed");
});
}
if (error != null)
{
throw new NSErrorException(error);
}
});
}
else
{
var itunesLink = new NSUrl("https://itunes.apple.com/us/genre/ios/id36?mt=8");
UIApplication.SharedApplication.OpenUrl(itunesLink, new NSDictionary() { }, null);
}
}
}
Problem : It doesn't throw any error. PresentViewController is called but it doesn't navigate/open my app in the App Store.
Thank you
Firstly, you don't need a custom renderer for this. You should inject a simple helper class that will open the appropriate app store for each platform you support.
Secondly, the url you are using for the iOS App Store looks to be incorrect. Use something like:
var url = new NSUrl($"https://itunes.apple.com/us/app/apple-store/{myAppId}?mt=8");
The app store URL used above is from the Apple docs. You can then open that url.

Suppress "yourdomain.com could not be found" dialog in GeckFX45

I am using GeckFX45 from NuGet to host a webpage for my OAuth2 login, During testing its behavior without internet connection I noticed that I get a dialog generated by the browser saying the URL could not be found. How can I suppress this to I can catch and handle the scenario in my app without alerting user?
My browser code is pretty standard, but for arguments sake included here anyway (Note I am using WPF not Win Forms hence the host control):
public OAuthLogin2(OAuthActions action, string args = null)
{
this.action = action;
Gecko.Xpcom.Initialize("Firefox");
host = new WindowsFormsHost();
browser = new GeckoWebBrowser();
browser.DocumentCompleted += Browser_DocumentCompleted;
browser.Navigating += Browser_Navigating;
browser.NavigationError += Browser_NavigationError;
browser.NSSError += Browser_NSSError;
InitializeComponent();
host.Child = browser;
GridWeb.Children.Add(host);
}
I can add a PromptService, but this may not work depending on language;
public class NoPromptService : PromptService
{
public override void Alert(string dialogTitle, string text)
{
log.Warn(dialogTitle, new Exception(text));
if (text.EndsWith("could not be found. Please check the name and try again."))
{
// Do Whatever
}
}
}
Add this in constructor:
PromptFactory.PromptServiceCreator = () => new NoPromptService();

How to open settings programmatically in ios

I was searching for the Xamarin implementation of How to open settings programmatically
Vito-ziv answered it for objective C - what is the correct way to do this in C# for iOS in Xamarin Studio?
For current devices this is only possible in ios8 (ios9 not available at time of writing) (It used to be possible before ios5 apparently - see this blog post from Adrian Stevens at Xamarin - shout out to him for the inspiration for this answer)
To do it in ios8, I did it like this:
var settingsString = UIKit.UIApplication.OpenSettingsUrlString;
var url = new NSUrl (settingsString);
UIApplication.SharedApplication.OpenUrl (url);
Where the above code was called from a click event via delegate class in a UIAlertView click.
Since I am supporting ios7 too, to handle ios7 devices I did this, where the HandleLocationAuthorisation method is called when deciding whether to present a view controller - the user on ios8 and above can choose to go to the settings directly, whereas the user on ios7 has to go there manually.
This example below is checking for location services, but with trivial changes could easily be changed to check for other types of settings.
public bool HandleLocationAuthorisation ()
{
if (CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways) {
return true;
} else {
UIAlertView uiAlert;
//iOS 8 and above can redirect to settings from within the app
if (UIDevice.CurrentDevice.CheckSystemVersion(8,0)) {
uiAlert = new UIAlertView
("Location Services Required",
"",
null,
"Return To App","Open Settings");
uiAlert.Delegate = new OpenSettingsFromUiAlertViewDelegate();
uiAlert.Message = "Authorisation to use your location is required to use this feature of the app.";
//ios7 and below has to go there manually
} else {
uiAlert = new UIAlertView
("Location Services Required",
"Authorisation to use your location is required to use this feature of the app. To use this feature please go to the settings app and enable location services",
null,
"Ok");
}
uiAlert.Show ();
return false;
}
}
For completeness, here is the code for the event delgate referenced above:
public class OpenSettingsFromUiAlertViewDelegate : UIAlertViewDelegate {
public override void Clicked (UIAlertView alertview, nint buttonIndex)
{
if (buttonIndex == 1) {
var settingsString = UIKit.UIApplication.OpenSettingsUrlString;
var url = new NSUrl (settingsString);
UIApplication.SharedApplication.OpenUrl (url);
}
}
}
Hope this will help you. This is working in iPhone not sure about working on iPad.
var url = new NSUrl("prefs:root=Settings");
UIApplication.SharedApplication.OpenUrl(url);

Categories

Resources