Handle Toast Notification message on foreground and background of app - windows10 - push-notification

My app receives toast from PHP using WNS server. Now I want to perform some actions when clicking on toast as listed below.
When the app is not active - the user should redirect to a page on the app "ShowPage".
When the app is active - the toast should show two buttons "Show" and "Cancel". When clicking on Show button app should redirect to "ShowPage"
My current toast from PHP is
$toastMessage= '<?xml version="1.0" encoding="utf-8"?>'.
'<toast launch="">'.
'<visual baseUri="">'.
'<binding template="ToastGeneric">'.
'<text>'.$subtitle.'</text>'.
'</binding>'.
'</visual>'.
'<actions />'.
'</toast>';
And I'm calling below function on App.xaml.cs
private async void RegisterEngagementNotification()
{
StoreServicesEngagementManager engagementManager = StoreServicesEngagementManager.GetDefault();
await engagementManager.RegisterNotificationChannelAsync();
}

Please see the documentation for sending a local toast and handling activation. Everything applies there (other than you're sending the toast from your server, but otherwise adding buttons and handling activation remains the same).

I saw that you're using StoreServicesEngagementManager APIs, then I know you're sending toast notification from windows developer dashboard. So, if you want to your toast contains two buttons, you would need to add actions like the following:
Then, in your "App.xaml.cs" file, you would need to add some code to handle this option in OnActivated.
protected override void OnActivated(IActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
}
base.OnActivated(args);
var toastActivationArgs = args as ToastNotificationActivatedEventArgs;
if (toastActivationArgs.Argument =="ShowPage")
{
rootFrame.Navigate(typeof(ShowPage));
}
}

Related

Xamarin Forms AppShell How return to previous page

I see a lot of threads on this sort of subject but none seem to apply to my question. I don't have a navigation page, I have a hamburger menu -- so Push/PopAsync() would not appear to be the answer. And I don't want to go to a specific page, I want to go back to the previous page (whatever it was) so GoToAsync() would not appear to be the answer.
Xamarin app for Android and UWP with iOS somewhere in the future. The description of the problem below is specific to Android; it works a little differently on UWP.
I want to put a Help entry in the hamburger menu that will take the user to a help page in the default browser. Hamburger menu seems to only go to an app page, so I defined a "dummy" View page that displays "Loading ..." and issues Browser.OpenAsync() in its OnAppearing() method, and that pretty much works. The problem is that the user would expect that the Back button would take him or her to the page they were on before clicking Help. I tried a couple of things. I have gotten close with the following but it does not quite work correctly:
In each of my other Views' OnAppearing() I call a method that saves the value of Current.MainPage.CurrentItem in a static. Then in the Help page after the OpenAsync() I set Current.MainPage.CurrentItem to its setting from the last page before the Help page.
Console.WriteLine("#### HelpPage loading Web Help");
_ = State.DisplayHelpPage(this, "MainHelp"); // _ = await Browser.OpenAsync(uri, blo);
Console.WriteLine("#### HelpPage returning to previous page");
State.ReloadPreviousPage(); // Current.MainPage).CurrentItem = lastFlyoutItem;
It almost works. The first time I click Help in the hamburger menu I get
#### HelpPage loading Web Help
#### HelpPage returning to previous page
#### HelpPage loading Web Help
#### HelpPage returning to previous page
The Web page loads perfectly. But when I click the Back button it displays again. Obviously my OnAppearing() method has been driven twice, which I do not understand.
If I click the Back button again I come back to the previous page in the application just as I wanted. The next time I click Help in the Hamburger menu it takes me to my dummy View page with no Web page. Obviously, my OnAppearing() is not being driven at all. But after that it works perfectly. I can go to any app page, and click Help in the menu. I get the Web page, and the Back button takes me back to the app and the page. In UWP of course the browser does not load on top of the app Views, and I seem to see it being loaded twice every time.
So ... what should I be doing differently? And why is my OnAppearing() being driven twice and then not at all ... and thereafter as I would expect?
There are several parts to this answer:
Get the previous page on to the Navigation stack. This is done by intercepting the Route "//HelpPage", and replacing it with a route that ISN'T a child of Shell.
Remember "FakePageVisible", so we know to do "PopAsync" in OnResume, when app returns from browser.
(Optional) "Entering" flag prevents going to browser twice.
App.xaml.cs:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
protected override void OnResume()
{
if (HelpPage.FakePageVisible) {
HelpPage.FakePageVisible = false;
var shell = MainPage as AppShell;
if (shell != null) {
shell.Navigation.PopAsync();
}
}
}
}
AppShell.xaml.cs:
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell()
{
InitializeComponent();
// Define a route that isn't a child of Shell.
Routing.RegisterRoute("Help2", typeof(HelpPage));
}
protected override void OnNavigating(ShellNavigatingEventArgs args)
{
base.OnNavigating(args);
if (args.Current != null) {
if (args.Source == ShellNavigationSource.ShellItemChanged) {
if (args.Target.Location.OriginalString == "//HelpPage") {
// Cancel the original route.
args.Cancel();
Device.BeginInvokeOnMainThread(() => {
// Used by the next OnAppearing.
HelpPage.Entering = true;
// Go there by a route that isn't a child of Shell.
// Doing so, pushes our previous location on to Navigation stack.
Shell.Current.GoToAsync("Help2");
});
}
}
}
}
}
HelpPage.xaml.cs:
public partial class HelpPage : ContentPage
{
public static bool Entering;
public static bool FakePageVisible;
protected override void OnAppearing
{
// Make sure this only happens once (just in case).
if (Entering) {
Entering = false;
FakePageVisible = true;
Xamarin.Essentials.Browser.OpenAsync("https://aka.ms/xamarin-quickstart");
}
}
}
For a simple demo, this code communicates via static variables in HelpPage. Re-factor as appropriate for your situation.

Prism navigation usage from Xamarin Forms App.OnAppLinkRequestReceived

I am building an app that makes use of some App links. In below example, I want to open a ResetPasswordPage when the user activates a link from an e-mail.
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("/NavigationPage/LoginPage");
}
protected override void OnAppLinkRequestReceived(Uri uri)
{
if (uri.Host.EndsWith("site.nl", StringComparison.OrdinalIgnoreCase))
{
if (uri.AbsolutePath.StartsWith(#"/appname/resetpassword/"))
{
if (uri.Segments.Length == 4)
{
string resetCode = uri.Segments[3];
NavigationParameters param = new();
param.Add(NavConst.PasswordResetCode, resetCode);
await NavigationService.NavigateAsync("ResetPasswordPage", param);
}
}
}
}
This code works, but not what I would expect (for both iOS and Android). I would expect that ResetPasswordPage would be added to the navigation stack that was set in OnInitialized, so that it would contain a back button in the navigation bar, bringing the user back to the login page in this case.
But instead, it looks like the navigation stack gets replaced. When ResetPasswordPage is shown, and the user clicks the hardware back button, the app is closed. However, i want it to go back to where the user was before.
Am I not understanding some navigation concepts well or might this be a bug?
The NavigationService is a rather unique service within Prism for Xamarin.Forms. It is constructed new for each page as navigation and the navigation stack is dependent on where you are navigating from.
For instance when you Navigate from the context of a MasterDetailPage / FlyoutPage, it understands that you aren't trying to push a modal on top of that page but instead you are starting from the context of Detail/Flyout. If the NavigationService continually updated the Page context then the navigation service injected into that MasterDetailPage/FlyoutPage's ViewModel would have no idea that it needed that page as the context to navigate from.
Your issue here ultimately is that you are Navigating from the context of the Application which has a NavigationService with no Page set. As a result even a relative Navigation will still have the effect of an absolute Navigation thus resetting the Application.MainPage since that NavigationService does not have the contextual understanding of another page.
Depending on your business requirements you have a few different options. One is to do an absolute Navigation that gives you navigation stack that you're looking for.
Optionally you may want to get the currently displayed page and update the NavigationService to understand it. In the App class you might add something like:
protected override void OnAppLinkRequestReceived(Uri uri)
{
if (NavigationService is IPageAware pa)
{
page.Page = PageUtilities.GetCurrentPage(MainPage);
}
if (someCondition)
{
// This will now navigate from relatively from the page
// returned by GetCurrentPage.
NavigationService.NavigateAsync("SomeRelativeUrl")
.OnNavigationError(HandleNavigationError);
}
}

Xamarin.forms ios open dialer without alert box

I am using Xamarin.Essentials Phone Dialer in my xamarin forms app to make calls. In android when we click call it will directly open dialer. In ios when we click call it will firstly show a alert box that will ask Call or cancel. Is it possible to open the dialer in ios without showing the popup? If it is not possible can we get the alert box cancel or call click event?
Unfortunately, this is not possible in iOS.
From Apple's documentation:
openURL
When a third party application invokes openURL: on a tel://,
facetime://, or facetime-audio:// URL, iOS displays a prompt and
requires user confirmation before dialing.
For security reasons, iOS requires users to confirm they want to perform the call before dialing in.
Is it possible to open the dialer in ios without showing the popup?
No, it is by design.
can we get the alert box cancel or call click event
You have to detect the state of phone call by yourself in the iOS project, you can write below codes in the AppDelegate:
CXCallObserver callObserver = new CXCallObserver();
callObserver.SetDelegate(new myDelegate(), DispatchQueue.MainQueue);
And in the delegate, you can check the call state:
public class myDelegate : ICXCallObserverDelegate
{
public IntPtr Handle => throw new NotImplementedException();
public void CallChanged(CXCallObserver callObserver, CXCall call)
{
if (call.Outgoing==true && call.HasConnected ==false)
{
Console.WriteLine("Dialing");
//use Messaging Center to send the state to Xamarin.forms Project.
}
if (call.Outgoing ==false && call.HasConnected == false)
{
Console.WriteLine("disconnect");
//use Messaging Center to send the state to Xamarin.forms Project.
}
//you can use other state to check the state...
//call.OnHold; call.HasEnded;
}
public void Dispose()
{
}
}

JavaFx WebView - how to scroll it to the bottom

I have made a conversation stage in JavaFx with a WebView that is displaying whole conversation. I want to show it from the bottom where the newest messages are so I used a function "execute script":
public void initialize() throws SQLException
{
buildText();
conversationText.getEngine().loadContent(text1);
conversationText.getEngine().executeScript("window.scrollTo(0, document.body.scrollHeight);");
conversationText.getEngine().setUserStyleSheetLocation(getClass().getResource("/html/style.css").toExternalForm());
}
I wrote this function in an initialize() void but it doesn't work, when I open the conversation it shows me all from the top where the eldest messages are :
But what is interesting it works properly when I use this metod form a void that is connected with a button named "script"
public void script()
{
conversationText.getEngine().executeScript("window.scrollTo(0, document.body.scrollHeight);");
}
when I press this button it scrolls to the bottom :
Is there any way to do this correct in an initialize void ? I have tried many ways like inserting the button method script() into initialize but i noticed that if I use function webengine.loadcontent() it always displays me all from the top.
I want to show the user the newest messages at the bottom from the beginning , not after pushing a button.

Xamarin.Forms, using the Prism.Forms NavigationService

I am trying to implement a login scenario with Xamarin Forms and Prism.
My application root page is a MasterDetail page (HomeMasterDetailPage); this being used to provide the side menu.
What I am trying to do is to have the HomeMasterDetailPage page shown with Content set to an instance of LoginPage. When the user logs in, the Content should change to an instance of HomeDetailContentPage. It should not be possible to return to the LoginPage using the back button.
I am using the NavigationService from Prism Forms.
My start up is like this...
public partial class App : PrismApplication {
public App(IPlatformInitializer initializer = null)
: base(initializer) {
}
protected override void OnInitialized() {
InitializeComponent();
Uri uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(HomeDetailContentPage)}", UriKind.Absolute);
var settings = Container.Resolve<SettingsService>();
if (!settings.DeviceUserID.HasValue)
uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(LoginPage)}", UriKind.Absolute);
NavigationService.NavigateAsync(uri);
}
protected override void RegisterTypes() {
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<LoginPage, LoginPageViewModel>();
Container.RegisterTypeForNavigation<HomeMasterDetailPage, HomePageViewModel>();
Container.RegisterTypeForNavigation<HomeMasterContentPage>();
Container.RegisterTypeForNavigation<HomeDetailContentPage>();
}
}
This correctly displays the LoginPage within the HomeMasterDetailPage if the user is not already logged on, and the HomeDetailContentPage within the same HomeMasterDetailPage if the user is logged on.
The problem comes when actually logging on; the following is the code within the view model for the LoginPage...
protected async Task ExecuteLoginCommand() {
Uri uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(HomeDetailContentPage)}", UriKind.Absolute);
await this.navigationService.NavigateAsync(uri);
}
It is my understanding that by using an absolute Uri this should reset the navigation so that the new page arrangement (/NavigationPage/HomeMasterDetailPage/HomeDetailContentPage) is at the top and bottom of the stack, allowing me to then move forward from there. Instead, I am getting an unhandled exception (on Android 7.0).
What am I doing wrong?
Note: All of the examples that I see have the MasterDetailPage at the root with Content being set to NavigationPage/ContentPage; when I try this I do not get exceptions, however the side menu operates differently within the MasterDetailPage - when you tap on the "hamburger" the side menu slides in over everything (including the action/title bar) and the only way to clear it is to tap outside of the side menu that slides in whereas when I show the MasterDetailPage within a NavigationPage the side menu slides in below the action/title bar and the hamburger changes to an arrow that you can click to hide the side menu again.
I can't say I've tested this exact scenario. However, if your LoginPage is the Detail of a MasterDetailPage, you may not want to perform the navigation from the LoginPage. You could, instead create a SuccessfulLoginEvent, which you would then publish from the LoginPage using the IEventAggregator, and you would subscribe to on your MasterDetailPage's ViewModel. You could then perform the navigation as _navigationService.NavigateAsync("NavigationPage/ViewA") and it should reset the Detail such that the back button doesn't bring you to the LoginPage.
Alternatively, you can simply perform an absolute Navigation which has the effect of:
Application.Current.MainPage = new MyPage().
To do to this, you could do the navigation from anywhere as:
_navigationService.NavigateAsync("/MyMasterDetailPage/NavigationPage/ViewA")

Resources