How to suppress push toast notification only when App in foreground? - c#

Note: I have only tested this using the emulator, and pushing toasts using the built in functionality. I am assuming this isn't an emulator issue.
I followed this guide in order to intercept push toast notifications while the app is running. However, I only want to suppress the toast notification when the app is in the foreground. It should still display when another app is in the foreground. So I wrote the following handler in App.xaml.cs (and subscribed to the PushNotificationReceived event):
private async void OnPushNotification(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
string msg = "";
if (e.NotificationType == PushNotificationType.Toast)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if (Window.Current.Visible)
{
msg += " Toast canceled.";
e.ToastNotification.SuppressPopup = true;
}
});
if (true) // actually determines if it's a certain type of toast
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
ConfirmationContentDialog confirmationDialog = new ConfirmationContentDialog();
confirmationDialog.SetMessage("Please confirm that you like turtles." + msg);
await confirmationDialog.ShowAsync();
});
}
}
}
So this works, in the sense that I only see the "toast canceled" message when the app was in the foreground when receiving the push notification. When I'm on the start screen or somewhere else I always get the toast. This is good. However, when the app is in the foreground, sometimes (usually after sending the second push) the toast shows up anyway (even though "Toast canceled" displays). But sometimes it doesn't. It's rather inconsistent.
This is leading me to believe that due to the await, sometimes the toast gets through before the code gets run on the UI thread to check whether the app is visible or not. However, I can't access Window.Current.Visible from here without using the dispatcher. I even tried CoreApplication.MainView.CoreWindow.Visible but that gives me "interface marshalled for different thread etc" exception. Speaking of which, I don't understand how CoreApplication.MainView.CoreWindow.Dispatcher can be called from anywhere but CoreApplication.MainView.CoreWindow.Visible not? How does that even work.
Anyway, how do I fix this? I would like to keep this within App.xaml.cs because I have a number of pages in this app, but this content dialog needs to be shown no matter which page the user is on, and without the user being redirected to a different page. However, I am of course open for new suggestions.

I fixed this as per Kai Brummund's suggestion by using a simple boolean toggle in the App class, and subscribing to the VisibilityChanged event like so:
private bool APP_VISIBLE = true;
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
// Stuff put here by Visual Studio
Window.Current.VisibilityChanged += OnVisibilityChanged;
Window.Current.Activate();
}
private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
if (e.Visible)
APP_VISIBLE = true;
else
APP_VISIBLE = false;
}
That way I can use APP_VISIBLE to suppress the popup without having to use the dispatcher and the toast is suppressed immediately.

Related

HandleNotificationOpened are not handled if app is closed before i open the notification

HandleNotificationOpened is working perfectly if App is on the background or running but it's not fired if i open the notification when app is closed.
I've tried to persist the data from event with SecureStorage, because i'm not sure if the event run but in the wrong time or it doesn't run at all.
public App()
{
OneSignal.Current.StartInit("onesignal-id").HandleNotificationOpened(HandleNotificationOpened).HandleNotificationReceived(HandleNotificationReceived).EndInit();
}
private async void HandleNotificationOpened(OSNotificationOpenedResult result)
{
var data = result.notification.payload.additionalData;
if (data != null)
{
data.TryGetValue("Title", out object Title);
data.TryGetValue("Conteudo", out object Conteudo);
data.TryGetValue("Link", out object RLink);
string lastvar = (Title.ToString().GetHashCode() + Conteudo.ToString().GetHashCode() + RLink.ToString().GetHashCode()).ToString();
if (!ChecarDB(lastvar))
{
InserirDB(Title.ToString(), Conteudo.ToString(), RLink.ToString());
}
await SecureStorage.SetAsync("UrlFromPush", RLink.ToString());
var page = new MainPage();
MessagingCenter.Send<MainPage>(page, "MudarURL");
}
}
Expected result is the application properly handle the event, No error messages at all.
This method will not be called when app is closed.
Although I did not use OneSignal to push notifications , according to the Android/iOS system notification processing mechanism, when the app is closed, when the remote notification is received, the click notification will restart the app, and the notification processing mechanism is processed by the system tray.
So the HandleNotificationOpened method will not be called.
I've solved this issue using URI Scheme to access early init background data, replacing the HandleNotificationOpened method for Intent?.Data?.EncodedQuery; with custom formatting method, that's how i got the expected result.

WP8 custom message box crash

I have made a custom messagebox (discussed here) that shows localized quit prompt.
protected override void OnBackKeyPress(CancelEventArgs e)
{
//some conditions
e.Cancel = true;
string quitText = DeviceWrapper.Localize("QUIT_TEXT");
string quitCaption = DeviceWrapper.Localize("QUIT_CAPTION");
string quitOk = DeviceWrapper.Localize("DISMISS");
string quitCancel = DeviceWrapper.Localize("MESSAGEBOX_CANCEL");
IAsyncResult asyncResult = Guide.BeginShowMessageBox(
quitCaption, quitText, new List<string> { quitOk, quitCancel },
0, MessageBoxIcon.Error, null, null);
asyncResult.AsyncWaitHandle.WaitOne();
int? result = Guide.EndShowMessageBox(asyncResult);
if (result.HasValue && result.Value == 0)
e.Cancel = false;
//some more features
}
It works fine, but it does crash when it's run as stand-alone (without Visual Studio) after few seconds if user doesn't press anything.
I tried reproducing the same crash with phone attached using Release and Debug from Visual Studion, but it's as stable as it can possibly be. It only crashes when app is run from the phone itself.
How do I find the issue? What could be the reason for it? As far as I can tell, I cannot access crash logs on the device.
There should be something with the messagebox logic perhaps?
UPD
Few notes on your suggestion, Neil:
first of all VS warns me "the async method lacks await" stuff.
secondly I am not sure how to return info to "BackKeyPressed" that e.Cancel should equal "false". This should be the safe way to quit an app AFAIK.
And wouldn't we quit the "OnBackKeyPress" method if we run async method from it? That would mean that we can't let it know about our decision (e.Cancel = false).
Or if we don't quit the "OnBackKeyPress" then it could mean this event will stay for too long and the whole purpose is lost - app will be killed, right?
UPD2:
It seems that I am between Scylla and Charybdis: either I show a messagebox and experience crashes in runtime which is discouraged by guidelines, or I don't show a messagebox at all.
I've tried both native MessageBox and my implementation of custom messagebox with localized "Ok" and "Cancel" buttons. They behave the same: either they are synchronous and hang OnBackKeyPress, or they are async and OnBackKeyPress exits before we can let it know about user's decision.
Final decision
Apparently the guidelines state that we shouldn't ask user's confirmation at all.
Since there is no viable way to implement a working quit confirmation messagebox without crashing I've decided to not show it at all.
If you block or delay certain page events for too long, the OS will kill your app as it assumes its crashed...
OnNavigatedTo
OnNavigatedFrom
OnBackKeyPress
In your case, I would recommend putting the custom MessageBox in it's own method, then calling it on the UI thread.
private void customMessageBox()
{
// custom message box code
// use NavigationService.GoBack() if you need to exit
}
protected override void OnBackKeyPress(CancelEventArgs e)
{
e.Cancel = true;
Deployment.Current.Dispatcher.BeginInvoke(() => customMessageBox() );
}
I also found another answer which accomplishes the thing by making the method Async.
Debugger attached (or not)
When the Visual Studio debugger is attached, the OS does not warn you of certain errors such as high memory usage, page init delays, etc - so it is very important to test your app without the Visual Studio debugger attached either on device or in the emulator (Debug or Release)
Windows Phone 8.1
Be aware that the handling of the Back button has changed in WP8.1 XAML/WinRT apps. It might be something to consider if you're upgrading the app project in future.
I implemented this in my project and it worked. Try it!
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
e.Cancel = true;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBoxResult result = MessageBox.Show("Hello", "Msg Box", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
//Do something
}
else
{
//Do something
}
});
}

Windows Phone - Store Universal App MessageDialog on launch

I want to show messagedialog when app starts. But in Universal app this code won't work.
I want to ask user for review.
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
var composite = localSettings.Values["askforreview"];
if (composite == null)
{
localSettings.Values.Add("askforreview", true);
composite = true;
}
bool askforreview = Convert.ToBoolean(composite);
if (askforreview)
{
MessageDialog dialog = new MessageDialog("some message");
dialog .Commands.Add(new UICommand("Yes", ( command) =>
{
Launcher.LaunchUriAsync(CurrentApp.LinkUri);
}));
dialog.Commands.Add(new UICommand("Not Now"));
await dialog .ShowAsync();
}
When I debug app, I always get an error "a.ShowAsnyc" statement. Program stops in App.g.i.cs's this statement.
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
This type of exception (UnauthorizedAccessException - Access Denied) when it comes to MessageDialogs usually happens when you already have one MessageDialog opened when you try to open another one.
I was able to get your code to work on my side in both page constructor (without the await, though), and in page loaded async event handler. But if I tried to do it in two places one after another, it would throw an exception, for the reason mentioned above.
So, please check that you don't have another MessageDialog opened when you try to show this one. Did you perhaps leave this code in both the page constructor and the app launched event handler? That might cause it.

Windows Phone 8 notifications and background tasks

I have searched the official forums and documentation and all other places, but haven't been able to find a concrete answer.
Q. In Windows Phone 8, is it possible for an application to respond to a Push Notification, and perform a task, while it is in the background?
As far as I can understand, for Toast and Tile Notifications, when the app is not in the foreground, there are no hooks at all for it to be able to respond to the incoming message.
I think "raw notifications" are the correct choice for this, since I am not required to update the app tile, or even show a Toast Notification. But, I haven't been able to find an example, or in the documentations, if I can do this.
I have found several links which talk about doing this for Windows store apps, but I want to find out if this can be done for Windows Phone 8.
I have checked this other thread,
Windows Phone 8 Background Task with notifications
Where one of the answer suggests that Whatsapp actually has a hack for this, to download the messages after a push notification is received. So, is the answer to my question, a NO?
Answer to your question is not exactly "NO",
and you are right whatsapp uses hack for this,
whatsapp someyow use AudioAgent, as they are allowed to run in background,
i dont know how exactly they do it, i am also searching for the same answer, let's see if i find something will post here
This has changed in Windows Phone 8.1. From Raw notification overview (Windows Runtime apps)
Receiving a raw notification
There are two avenues through which your app can be receive raw
notifications:
Through notification delivery events while your application is running.
Through background tasks triggered by the raw notification if your app is enabled to run background tasks.
An app can use both mechanisms to receive raw notifications. If an app
implements both the notification delivery event handler and background
tasks that are triggered by raw notifications, the notification
delivery event will take priority when the app is running.
If the app is running, the notification delivery event will take priority over the background task and the app will have the first opportunity to process the notification.
The notification delivery event handler can specify, by setting the event's PushNotificationReceivedEventArgs.Cancel property to true, that the raw notification should not be passed to its background task once the handler exits. If the Cancel property is set to false or is not set (the default value is false), the raw notification will trigger the background task after the notification delivery event handler has done its work.
Here is a complete guide on receiving push notifications in the background for Windows Phone 8.1:
Get a push notifications channel URI:
PushNotificationChannel _channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
string ChannelUri = _channel.Uri;
Make sure you actually get the URI by logging it. Save the URI and run this on every app launch as the URI gets updated quite frequently.
Create a Windows Runtime Component project inside your solution:
Right click on solution -> Add -> New Project -> select "Windows Runtime Component (Windows Phone)". Call this project "Tasks" or whatever you prefer.
Create a new class extending an IBackgroundTask inside your newly created project. I called mine "NotificationReceiver":
using Windows.ApplicationModel.Background;
namespace Tasks {
public sealed class NotificationReceiver : IBackgroundTask {
public void Run(IBackgroundTaskInstance taskInstance) {
// TODO: implement your task here
}
}
}
Your task will be implemented here inside "Run" function.
Reference your Runtime Component in your main project:
Click on your Windows Phone project -> right click on "References" -> Add Reference -> Tick your Runtime Component and press OK.
Edit your app manifest:
Double-click on your package manifest -> Declarations -> add "Location" and "Push notification" to Supported task types, add your background task class name to Entry point: mine is called "Tasks.NotificationReceiver". Save your changes.
Unregister and register your background task every time the app is launched. I am giving the complete solution, just call "setupBackgroundTask()":
private void setupBackgroundTask() {
requestAccess();
UnregisterBackgroundTask();
RegisterBackgroundTask();
}
private void RegisterBackgroundTask() {
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder();
PushNotificationTrigger trigger = new PushNotificationTrigger();
taskBuilder.SetTrigger(trigger);
taskBuilder.TaskEntryPoint = "Tasks.NotificationReceiver";
taskBuilder.Name = "pushTask";
try {
BackgroundTaskRegistration task = taskBuilder.Register();
Logger.log("TASK REGISTERED");
} catch (Exception ex) {
Logger.log("FAILED TO REGISTER TASK");
UnregisterBackgroundTask();
}
}
private bool UnregisterBackgroundTask() {
foreach (var iter in BackgroundTaskRegistration.AllTasks) {
IBackgroundTaskRegistration task = iter.Value;
if (task.Name == "pushTask") {
task.Unregister(true);
Logger.log("TASK UNREGISTERED");
return true;
}
}
return false;
}
private async void requestAccess() {
BackgroundAccessStatus backgroundStatus = await BackgroundExecutionManager.RequestAccessAsync();
}
Get RawNotification object inside your task:
RawNotification notification = (RawNotification) taskInstance.TriggerDetails;

How to display a message in Windows Store Apps?

How to display a message box in windows 8 apps using c# like calling MessageBox.Show() in windows phone 7?
MessageDialog msgDialog = new MessageDialog("Your message", "Your title");
//OK Button
UICommand okBtn = new UICommand("OK");
okBtn.Invoked = OkBtnClick;
msgDialog.Commands.Add(okBtn);
//Cancel Button
UICommand cancelBtn = new UICommand("Cancel");
cancelBtn.Invoked = CancelBtnClick;
msgDialog.Commands.Add(cancelBtn);
//Show message
msgDialog.ShowAsync();
And your call backs
private void CancelBtnClick(IUICommand command)
{
}
private void OkBtnClick(IUICommand command)
{
}
P.S. You can follow this tutorial for more.
The MessageDialog class should fit your needs.
My simpler way, for confirmation type message boxes:
var dlg = new MessageDialog("Are you sure?");
dlg.Commands.Add(new UICommand("Yes", null, "YES"));
dlg.Commands.Add(new UICommand("No", null, "NO"));
var op = await dlg.ShowAsync();
if ((string)op.Id == "YES")
{
//Do something
}
For simpler way, Just to display the message text and OK button. Use Windows.UI.Popups namespace. Create a method messagebox() that method should be
using Windows.UI.Popups;
protected async void messageBox(string msg)
{
var msgDlg = new Windows.UI.Popups.MessageDialog(msg);
msgDlg.DefaultCommandIndex = 1;
await msgDlg.ShowAsync();
}
Then call this method in your code like
messageBox("Unexpected error held");
Additional tidbit:
It appears in a modern Windows App a MessageDialog will not show prior to your app making its Window.Current.Active() call, which usually happens in the app class' OnLaunched() method. If you're trying to use MessageDialog to display something like a start-up exception, that's important to keep in mind.
My testing indicates MessageDialog.ShowAsync() may actually await but without the dialog being shown if Window.Current.Active() hasn't been called yet, so from a code execution standpoint it'll look like everything is working but yet no dialog is displayed.
If the goal is to display an exception during start-up, I can think of two options (there may be more).
Capture the exception information and then wait to display it until after Window.Current.Activate(). This can work if the exception is such that the application can recover from it and continue with start-up. For example, if there is an error restoring saved state information the app might want to report that to the user but then continue to start-up as if there was no saved state.
If the situation is such that the app is throwing up its hands and intending to terminate, but wants to let the user know what happened, then another solution might be to have a separate dedicated code block/method that plugs a new clean frame into Windows.Current.Content, activates it using Windows.Current.Activate(), and then invokes MessageDialog.ShowAsync(). I haven't experimented with this approach so I'm not sure if other conditions also need to be met like possibly loading a page into the frame.
use for page like:
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
Windows.UI.Popups.MessageDialog a = new Windows.UI.Popups.MessageDialog("hello this is awth");
await a.ShowAsync();
}

Categories

Resources